├── .github ├── ISSUE_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── developer-experience-issue.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md └── no-response.yml ├── .travis.yml ├── Api ├── CardManagementInterface.php └── Data │ └── CardInterface.php ├── Block ├── Adminhtml │ └── System │ │ └── Config │ │ └── LinkBuilder.php ├── Dashboard.php ├── Dashboard │ ├── AbstractClass.php │ ├── AverageOrder.php │ ├── AverageOrderValue.php │ ├── Bestsellers.php │ ├── Card.php │ ├── ConversionFunnel.php │ ├── Customers.php │ ├── LastOrders.php │ ├── LastSearches.php │ ├── LifetimeSales.php │ ├── MostViewedProducts.php │ ├── NewCustomers.php │ ├── Orders.php │ ├── RepeatCustomerRate.php │ ├── SalesByLocation.php │ ├── Shipping.php │ ├── Tax.php │ ├── TopSearches.php │ └── TotalSales.php ├── Menu.php ├── Store │ └── Switcher.php └── SwitchToNew.php ├── CHANGELOG ├── Controller ├── Adminhtml │ ├── Cards │ │ ├── LoadCard.php │ │ └── SavePosition.php │ ├── Dashboard │ │ ├── Index.php │ │ ├── RefreshStatistics.php │ │ └── SwitchToNew.php │ └── Details │ │ └── StoreFilter.php ├── Cards │ ├── LoadCard.php │ └── SavePosition.php └── Dashboard │ └── Index.php ├── Helper └── Data.php ├── LICENSE ├── Model ├── Api │ ├── CardManagement.php │ └── Data │ │ └── Card.php ├── CardsManageFactory.php └── ResourceModel │ └── Viewed │ └── Collection.php ├── Plugin ├── BackendLogoUrl.php ├── Config.php ├── Framework │ └── Reflection │ │ └── DataObjectProcessor.php ├── Model │ └── StartupPageUrl.php └── MoveMenu.php ├── README.md ├── USER-GUIDE.md ├── UserGuide.pdf ├── composer.json ├── etc ├── acl.xml ├── adminhtml │ ├── di.xml │ ├── menu.xml │ ├── routes.xml │ └── system.xml ├── config.xml ├── di.xml ├── frontend │ └── routes.xml ├── module.xml └── webapi.xml ├── i18n └── en_US.csv ├── registration.php └── view ├── adminhtml ├── layout │ ├── adminhtml_dashboard_index.xml │ ├── adminhtml_system_config_edit.xml │ ├── mpreports_dashboard_index.xml │ └── store_switcher.xml ├── templates │ ├── dashboard.phtml │ ├── dashboard │ │ ├── card.phtml │ │ ├── chart.phtml │ │ ├── conversion_funnel.phtml │ │ └── sales_by_location.phtml │ ├── menu.phtml │ ├── popup.phtml │ ├── store │ │ └── switcher.phtml │ ├── switch.phtml │ └── system │ │ └── config │ │ └── link_builder.phtml └── web │ ├── css │ ├── detail │ │ └── menu.css │ └── source │ │ └── _module.less │ └── js │ └── detail │ └── menu.js ├── base ├── requirejs-config.js └── web │ ├── css │ ├── dashboard │ │ └── custom.css │ ├── images │ │ └── conversion_funnel │ │ │ └── funnel-container.png │ └── lib │ │ ├── daterangepicker.css │ │ └── gridstack.css │ └── js │ ├── dashboard │ ├── initChart.js │ ├── initDateRange.js │ └── initGridStack.js │ └── lib │ ├── Chart.bundle.min.js │ ├── daterangepicker.min.js │ ├── gridstack.jQueryUI.js │ ├── gridstack.js │ └── moment.min.js └── frontend ├── layout └── mpreports_dashboard_index.xml └── web ├── css ├── dashboard │ └── styles.css ├── fonts │ ├── admin-icons │ │ ├── admin-icons.eot │ │ ├── admin-icons.svg │ │ ├── admin-icons.ttf │ │ ├── admin-icons.woff │ │ ├── admin-icons.woff2 │ │ └── selection.json │ └── opensans │ │ ├── bold │ │ ├── opensans-700.eot │ │ ├── opensans-700.svg │ │ ├── opensans-700.ttf │ │ ├── opensans-700.woff │ │ └── opensans-700.woff2 │ │ ├── light │ │ ├── opensans-300.eot │ │ ├── opensans-300.svg │ │ ├── opensans-300.ttf │ │ ├── opensans-300.woff │ │ └── opensans-300.woff2 │ │ ├── regular │ │ ├── opensans-400.eot │ │ ├── opensans-400.svg │ │ ├── opensans-400.ttf │ │ ├── opensans-400.woff │ │ └── opensans-400.woff2 │ │ └── semibold │ │ ├── opensans-600.eot │ │ ├── opensans-600.svg │ │ ├── opensans-600.ttf │ │ ├── opensans-600.woff │ │ └── opensans-600.woff2 └── images │ └── loader-1.gif └── js └── store-switcher.js /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### Preconditions 4 | 5 | 6 | 1. Magento: 7 | 2. PHP: 8 | 3. MySQL: 9 | 4. 10 | 11 | ### Steps to reproduce 12 | 13 | 1. 14 | 2. 15 | 3. 16 | 17 | ### Expected result 18 | 19 | 1. 20 | 21 | ### Actual result 22 | 23 | 1. [Screenshot, logs] 24 | 25 | 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Technical issue with the extension 4 | labels: 'Issue' 5 | 6 | --- 7 | 8 | 11 | 12 | ### Preconditions (*) 13 | 16 | 1. 17 | 2. 18 | 19 | ### Steps to reproduce (*) 20 | 23 | 1. 24 | 2. 25 | 26 | ### Expected result (*) 27 | 28 | 1. [Screenshots, logs or description] 29 | 2. 30 | 31 | ### Actual result (*) 32 | 33 | 1. [Screenshots, logs or description] 34 | 2. 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/developer-experience-issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Developer experience issue 3 | about: Issues related to customization, extensibility, modularity 4 | labels: 'Experience' 5 | 6 | --- 7 | 8 | 11 | 12 | ### Summary (*) 13 | 14 | 15 | ### Examples (*) 16 | 17 | 18 | ### Proposed solution 19 | 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Request feature for development 4 | labels: 'Feature Request' 5 | 6 | --- 7 | 8 | 11 | 12 | ### Description (*) 13 | 14 | 15 | ### Expected behavior (*) 16 | 17 | 18 | ### Benefits 19 | 20 | 21 | ### Additional information 22 | 23 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### Description 4 | 5 | 6 | ### Fixed Issues (if relevant) 7 | 8 | 1. https://github.com/mageplaza/magento-2-reports/: Issue title 9 | 2. ... 10 | 11 | ### Manual testing scenarios 12 | 13 | 1. 14 | 2. 15 | 16 | ### Contribution checklist 17 | - [ ] Pull request has a meaningful description of its purpose 18 | - [ ] All commits are accompanied by meaningful commit messages 19 | - [ ] All new or changed code is covered with unit/integration tests (if applicable) 20 | -------------------------------------------------------------------------------- /.github/no-response.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-no-response - https://github.com/probot/no-response 2 | 3 | # Number of days of inactivity before an Issue is closed for lack of response 4 | daysUntilClose: 7 5 | # Label requiring a response 6 | responseRequiredLabel: waiting-customer-response 7 | # Comment to post when closing an Issue for lack of response. Set to `false` to disable 8 | closeComment: > 9 | This issue has been automatically closed because there has been no response 10 | to our request for more information from the original author. With only the 11 | information that is currently in the issue, we don't have enough information 12 | to take action. Please reach out if you have or find the answers we need so 13 | that we can investigate further. 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - 7.0 4 | - 7.1 5 | sudo: required 6 | dist: trusty 7 | env: 8 | global: 9 | - COMPOSER_BIN_DIR=~/bin 10 | - INTEGRATION_SETS=3 11 | - NODE_JS_VERSION=6 12 | - MAGENTO_HOST_NAME="magento2.travis" 13 | - COMPOSER_MODULE=mageplaza/module-reports 14 | matrix: 15 | - MAGENTO_VERSION=2.2.1 TEST_SUITE=integration INTEGRATION_INDEX=1 16 | - MAGENTO_VERSION=2.2.1 TEST_SUITE=integration INTEGRATION_INDEX=2 17 | - MAGENTO_VERSION=2.2.1 TEST_SUITE=integration INTEGRATION_INDEX=3 18 | - MAGENTO_VERSION=2.2.2 TEST_SUITE=static 19 | - MAGENTO_VERSION=2.2.2 TEST_SUITE=js GRUNT_COMMAND=static 20 | - MAGENTO_VERSION=2.2.2 TEST_SUITE=integration INTEGRATION_INDEX=1 21 | - MAGENTO_VERSION=2.2.2 TEST_SUITE=integration INTEGRATION_INDEX=2 22 | - MAGENTO_VERSION=2.2.2 TEST_SUITE=integration INTEGRATION_INDEX=3 23 | - MAGENTO_VERSION=2.2.5 TEST_SUITE=integration INTEGRATION_INDEX=1 24 | - MAGENTO_VERSION=2.2.5 TEST_SUITE=integration INTEGRATION_INDEX=2 25 | - MAGENTO_VERSION=2.2.5 TEST_SUITE=integration INTEGRATION_INDEX=3 26 | - MAGENTO_VERSION=2.2.6 TEST_SUITE=integration INTEGRATION_INDEX=1 27 | - MAGENTO_VERSION=2.2.6 TEST_SUITE=integration INTEGRATION_INDEX=2 28 | - MAGENTO_VERSION=2.2.6 TEST_SUITE=integration INTEGRATION_INDEX=3 29 | 30 | matrix: 31 | exclude: 32 | - php: 7.0 33 | env: MAGENTO_VERSION=2.2.2 TEST_SUITE=js GRUNT_COMMAND=static 34 | - php: 7.0 35 | env: MAGENTO_VERSION=2.2.2 TEST_SUITE=static 36 | cache: 37 | apt: true 38 | directories: 39 | - "$HOME/.composer/cache" 40 | - "$HOME/.nvm" 41 | addons: 42 | apt: 43 | packages: 44 | - mysql-server-5.6 45 | - mysql-client-core-5.6 46 | - mysql-client-5.6 47 | - postfix 48 | firefox: '46.0' 49 | hosts: 50 | - magento2.travis 51 | before_install: 52 | - git clone https://github.com/magento/magento2 --branch $MAGENTO_VERSION 53 | - cd magento2 54 | - bash ./dev/travis/before_install.sh 55 | install: 56 | - composer install --no-interaction --prefer-dist 57 | - composer require $COMPOSER_MODULE 58 | before_script: 59 | #- cp -f ${TRAVIS_BUILD_DIR}/dev/tests/integration/phpunit.xml.dist dev/tests/integration/ 60 | - echo "vendor/$COMPOSER_MODULE" > dev/tests/static/testsuite/Magento/Test/Less/_files/whitelist/common.txt 61 | - echo "vendor/$COMPOSER_MODULE" > dev/tests/static/testsuite/Magento/Test/Php/_files/whitelist/common.txt 62 | - echo "vendor/$COMPOSER_MODULE/**/*.js" > dev/tests/static/testsuite/Magento/Test/Js/_files/whitelist/magento.txt 63 | - bash ./dev/travis/before_script.sh 64 | script: 65 | - test $TEST_SUITE = "static" && TEST_FILTER='--filter "Magento\\Test\\Php\\LiveCodeTest"' || true 66 | - test $TEST_SUITE = "functional" && TEST_FILTER='dev/tests/functional/testsuites/Magento/Mtf/TestSuite/InjectableTests.php' || true 67 | 68 | 69 | -------------------------------------------------------------------------------- /Api/CardManagementInterface.php: -------------------------------------------------------------------------------- 1 | helper = $helper; 66 | $this->url = $url; 67 | 68 | parent::__construct($context, $data); 69 | } 70 | 71 | /** 72 | * Unset scope 73 | * 74 | * @param AbstractElement $element 75 | * 76 | * @return string 77 | */ 78 | public function render(AbstractElement $element) 79 | { 80 | $element->unsScope(); 81 | 82 | return parent::render($element); 83 | } 84 | 85 | /** 86 | * Get the button Generate & Send 87 | * 88 | * @param AbstractElement $element 89 | * 90 | * @return string 91 | * @SuppressWarnings(Unused) 92 | */ 93 | protected function _getElementHtml(AbstractElement $element) 94 | { 95 | return $this->_toHtml(); 96 | } 97 | 98 | /** 99 | * @return bool 100 | */ 101 | public function isEnabled() 102 | { 103 | return $this->helper->isEnabled(); 104 | } 105 | 106 | /** 107 | * @return string 108 | */ 109 | public function getBuilderUrl() 110 | { 111 | return $this->url->getUrl('mpreports/dashboard/index', ['accessKey' => 'access_key', '_nosid' => true]); 112 | } 113 | 114 | /** 115 | * @return mixed 116 | */ 117 | public function getAccessKey() 118 | { 119 | return $this->helper->getConfigMobileAccessKey(); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Block/Dashboard.php: -------------------------------------------------------------------------------- 1 | _cardsManageFactory = $cardsManageFactory; 66 | $this->_helperData = $helperData; 67 | 68 | parent::__construct($context, $data); 69 | } 70 | 71 | /** 72 | * @return Template|void 73 | * @throws LocalizedException 74 | */ 75 | protected function _prepareLayout() 76 | { 77 | if ($this->isEnabled()) { 78 | foreach ($this->getMap() as $alias => $block) { 79 | $this->addChild($alias, $block); 80 | } 81 | $this->getLayout()->unsetElement('dashboard'); 82 | } else { 83 | $this->getLayout()->unsetElement('ar_dashboard'); 84 | } 85 | 86 | parent::_prepareLayout(); 87 | } 88 | 89 | /** 90 | * @return string 91 | */ 92 | public function getSwitchUrl() 93 | { 94 | if ($url = $this->getData('switch_url')) { 95 | return $url; 96 | } 97 | 98 | return $this->getUrl('adminhtml/*/*', ['_current' => true, 'period' => null]); 99 | } 100 | 101 | /** 102 | * @return int 103 | */ 104 | public function isCompare() 105 | { 106 | return $this->_helperData->isCompare() ? 1 : 0; 107 | } 108 | 109 | /** 110 | * @return bool 111 | */ 112 | public function isEnabled() 113 | { 114 | return $this->_helperData->isEnabled(); 115 | } 116 | 117 | /** 118 | * @return array 119 | */ 120 | public function getCards() 121 | { 122 | try { 123 | $result = $this->_cardsManageFactory->create(); 124 | } catch (Exception $e) { 125 | $result = []; 126 | $this->_logger->critical($e); 127 | } 128 | 129 | return $result; 130 | } 131 | 132 | /** 133 | * @return array 134 | */ 135 | protected function getMap() 136 | { 137 | return $this->_cardsManageFactory->getMap(); 138 | } 139 | 140 | /** 141 | * @return string 142 | * @throws Exception 143 | */ 144 | public function getDate() 145 | { 146 | return Data::jsonEncode($this->_helperData->getDateRange()); 147 | } 148 | 149 | /** 150 | * @return string 151 | */ 152 | public function getArea() 153 | { 154 | return 'adminhtml'; 155 | } 156 | 157 | /** 158 | * @return array 159 | * @return array 160 | */ 161 | public function getGridStackConfig() 162 | { 163 | $config = [ 164 | 'url' => $this->getUrl('mpreports/cards/saveposition', ['form_key' => $this->getFormKey()]), 165 | 'loadCardUrl' => $this->getUrl('mpreports/cards/loadcard', ['form_key' => $this->getFormKey()]) 166 | ]; 167 | 168 | return $config; 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /Block/Dashboard/AverageOrder.php: -------------------------------------------------------------------------------- 1 | _helperData->getLifetimeSales(); 52 | if (isset($total['average'])) { 53 | return $this->format($total['average'], [], $includeContainer); 54 | } 55 | 56 | return ''; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Block/Dashboard/AverageOrderValue.php: -------------------------------------------------------------------------------- 1 | _helperData->getDateRange(); 60 | $totals = $this->_helperData->getSalesByDateRange($date[0], $date[1]); 61 | 62 | return $this->getBaseCurrency()->format($totals->getAverage() ?: 0, [], $includeContainer); 63 | } 64 | 65 | /** 66 | * @return float|int 67 | * @throws LocalizedException 68 | * @throws Exception 69 | */ 70 | public function getRate() 71 | { 72 | $dates = $this->_helperData->getDateRange(); 73 | $totals = $this->_helperData->getSalesByDateRange($dates[0], $dates[1]); 74 | $compareTotals = $this->_helperData->getSalesByDateRange($dates[2], $dates[3]); 75 | if ((int) $totals->getAverage() === 0 && (int) $compareTotals->getAverage() === 0) { 76 | return 0; 77 | } 78 | if ((int) $compareTotals->getAverage() === 0) { 79 | return 100; 80 | } 81 | if ((int) $totals->getAverage() === 0) { 82 | return -100; 83 | } 84 | 85 | return round((($totals->getAverage() - $compareTotals->getAverage()) / $compareTotals->getAverage()) * 100, 2); 86 | } 87 | 88 | /** 89 | * @param $date 90 | * @param null $endDate 91 | * 92 | * @return float|int 93 | * @throws LocalizedException 94 | */ 95 | protected function getDataByDate($date, $endDate = null) 96 | { 97 | $totals = $this->_helperData->getSalesByDateRange($date, $endDate); 98 | 99 | return round($totals->getAverage() ?: 0, 2); 100 | } 101 | 102 | /** 103 | * @return string 104 | * @throws NoSuchEntityException 105 | */ 106 | protected function getYUnit() 107 | { 108 | return $this->getBasePriceFormat(); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Block/Dashboard/Bestsellers.php: -------------------------------------------------------------------------------- 1 | addChild('mp_bestsellers', Ordered::class); 42 | } 43 | 44 | /** 45 | * @return Phrase|string 46 | */ 47 | public function getTitle() 48 | { 49 | return __('Bestsellers'); 50 | } 51 | 52 | /** 53 | * @return bool 54 | */ 55 | public function canShowDetail() 56 | { 57 | return true; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Block/Dashboard/Card.php: -------------------------------------------------------------------------------- 1 | _helperData = $helperData; 57 | 58 | parent::__construct($context, $data); 59 | } 60 | 61 | /** 62 | * @return int 63 | */ 64 | public function isCompare() 65 | { 66 | return $this->_helperData->isCompare() ? 1 : 0; 67 | } 68 | 69 | /** 70 | * @return bool 71 | */ 72 | public function isEnabled() 73 | { 74 | return $this->_helperData->isEnabled(); 75 | } 76 | 77 | /** 78 | * @return mixed 79 | */ 80 | public function getCard() 81 | { 82 | return $this->getData('mp_card'); 83 | } 84 | 85 | /** 86 | * @param $card 87 | * 88 | * @return Card 89 | */ 90 | public function setCard($card) 91 | { 92 | return $this->setData('mp_card', $card); 93 | } 94 | 95 | /** 96 | * @return string 97 | */ 98 | public function getArea() 99 | { 100 | return 'adminhtml'; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Block/Dashboard/ConversionFunnel.php: -------------------------------------------------------------------------------- 1 | viewedCollectionFactory = $viewedCollectionFactory; 82 | $this->itemCollectionFactory = $itemCollectionFactory; 83 | $this->orderItemCollectionFactory = $orderItemCollectionFactory; 84 | 85 | parent::__construct($context, $helperData, $data); 86 | } 87 | 88 | /** 89 | * @return mixed 90 | * @throws Exception 91 | */ 92 | public function getProductViews() 93 | { 94 | $collection = $this->viewedCollectionFactory->create(); 95 | if ($storeId = $this->getStore()) { 96 | $collection->addFieldToFilter('store_id', $storeId); 97 | } 98 | 99 | $dateRange = $this->getDateRange(); 100 | if ($dateRange[0]) { 101 | $collection->addFieldToFilter('added_at', ['gteq' => $dateRange[0]]); 102 | } 103 | if ($dateRange[1]) { 104 | $collection->addFieldToFilter('added_at', ['lteq' => $dateRange[1]]); 105 | } 106 | 107 | return $collection->getSize(); 108 | } 109 | 110 | /** 111 | * @return mixed 112 | * @throws Exception 113 | */ 114 | public function getAllCartItems() 115 | { 116 | $collection = $this->itemCollectionFactory->create(); 117 | $collection = $this->addFilter($collection); 118 | 119 | return $collection->getSize(); 120 | } 121 | 122 | /** 123 | * @return mixed 124 | * @throws Exception 125 | */ 126 | public function getAllOrderItem() 127 | { 128 | $collection = $this->orderItemCollectionFactory->create(); 129 | $collection = $this->addFilter($collection); 130 | 131 | return $collection->getSize(); 132 | } 133 | 134 | /** 135 | * @param OrderItemCollection|ViewedCollection|Collection| $collection 136 | * 137 | * @return mixed 138 | * @throws Exception 139 | */ 140 | protected function addFilter($collection) 141 | { 142 | if ($storeId = $this->getStore()) { 143 | $collection->addFieldToFilter('store_id', $storeId); 144 | } 145 | 146 | $dateRange = $this->getDateRange(); 147 | if ($dateRange[0]) { 148 | $collection->addFieldToFilter('created_at', ['gteq' => $dateRange[0]]); 149 | } 150 | if ($dateRange[1]) { 151 | $collection->addFieldToFilter('created_at', ['lteq' => $dateRange[1]]); 152 | } 153 | 154 | return $collection; 155 | } 156 | 157 | /** 158 | * @param null $fromDate 159 | * @param null $toDate 160 | * 161 | * @return array 162 | * @throws Exception 163 | */ 164 | protected function getDateRange($fromDate = null, $toDate = null) 165 | { 166 | if ($fromDate === null) { 167 | $fromDate = isset($this->_request->getParam('mpFilter')['startDate']) 168 | ? $this->_request->getParam('mpFilter')['startDate'] 169 | : $this->_request->getParam('startDate'); 170 | } 171 | if ($toDate === null) { 172 | $toDate = isset($this->_request->getParam('mpFilter')['endDate']) 173 | ? $this->_request->getParam('mpFilter')['endDate'] 174 | : $this->_request->getParam('endDate'); 175 | } 176 | if ($toDate === null || $fromDate === null) { 177 | [$fromDate, $toDate] = $this->_helperData->getDateRange(); 178 | } 179 | 180 | return [$fromDate, $toDate]; 181 | } 182 | 183 | /** 184 | * @return int|mixed|null 185 | */ 186 | protected function getStore() 187 | { 188 | $storeParam = $this->_request->getParam('store') ?: 0; 189 | $storeFilterParam = isset($this->_request->getParam('mpFilter')['store']) 190 | ? $this->_request->getParam('mpFilter')['store'] : null; 191 | $storeId = ($storeFilterParam !== null && $storeFilterParam !== '') 192 | ? $storeFilterParam 193 | : $storeParam; 194 | 195 | return $storeId; 196 | } 197 | 198 | /** 199 | * @return Phrase|string 200 | */ 201 | public function getTitle() 202 | { 203 | return __('Conversion Funnel'); 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /Block/Dashboard/Customers.php: -------------------------------------------------------------------------------- 1 | _helperData->getLifetimeSales(); 52 | if (isset($total['lifetime'])) { 53 | return $this->format($total['lifetime'], [], $includeContainer); 54 | } 55 | 56 | return ''; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Block/Dashboard/MostViewedProducts.php: -------------------------------------------------------------------------------- 1 | _orderFactory = $orderFactory; 64 | 65 | parent::__construct($context, $helperData, $data); 66 | } 67 | 68 | /** 69 | * @param $date 70 | * @param null $endDate 71 | * 72 | * @return int 73 | * @throws LocalizedException*@throws \Exception 74 | * @throws Exception 75 | */ 76 | protected function getDataByDate($date, $endDate = null) 77 | { 78 | $collection = $this->_orderFactory->create()->getCollection(); 79 | $collection = $this->_helperData->addStoreFilter($collection); 80 | $collection = $this->_helperData->addStatusFilter($collection); 81 | $collection = $this->_helperData->addTimeFilter($collection, $date, $endDate); 82 | 83 | return $collection->getSize(); 84 | } 85 | 86 | /** 87 | * @return float|int 88 | * @throws LocalizedException 89 | * @throws Exception 90 | */ 91 | public function getRate() 92 | { 93 | $date = $this->_helperData->getDateRange(); 94 | $count = $this->getDataByDate($date[0], $date[1]); 95 | $countCompare = $this->getDataByDate($date[2], $date[3]); 96 | if ($countCompare === 0 && $count === 0) { 97 | return 0; 98 | } 99 | if ($countCompare === 0) { 100 | return 100; 101 | } 102 | if ($count === 0) { 103 | return -100; 104 | } 105 | $rate = ($count - $countCompare) * 100 / $countCompare; 106 | 107 | return round($rate, 2); 108 | } 109 | 110 | /** 111 | * @return int 112 | * @throws LocalizedException 113 | * @throws Exception 114 | */ 115 | public function getTotal() 116 | { 117 | $date = $this->_helperData->getDateRange(); 118 | 119 | return $this->getDataByDate($date[0], $date[1]); 120 | } 121 | 122 | /** 123 | * @return Phrase|string 124 | */ 125 | protected function getYLabel() 126 | { 127 | return __('Orders'); 128 | } 129 | 130 | /** 131 | * @return Phrase|string 132 | */ 133 | public function getTitle() 134 | { 135 | return __('Orders'); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /Block/Dashboard/SalesByLocation.php: -------------------------------------------------------------------------------- 1 | _countryFactory = $countryFactory; 73 | $this->_orderCollectionFactory = $orderCollectionFactory; 74 | 75 | parent::__construct($context, $helperData, $data); 76 | } 77 | 78 | /** 79 | * @param $startDate 80 | * @param $endDate 81 | * @param null $size 82 | * 83 | * @return array 84 | * @throws LocalizedException*@throws \Exception 85 | * @throws Exception 86 | */ 87 | protected function getDataByDateRange($startDate, $endDate, $size = null) 88 | { 89 | $data = []; 90 | /** @var Collection $collection */ 91 | $collection = $this->_orderCollectionFactory->create(); 92 | $collection = $this->_helperData->addStoreFilter($collection); 93 | $collection = $this->_helperData->addStatusFilter($collection); 94 | $collection = $this->_helperData->addTimeFilter($collection, $startDate, $endDate); 95 | $collection->getSelect()->join( 96 | ['soa' => $collection->getTable('sales_order_address')], 97 | "main_table.entity_id=soa.parent_id AND soa.address_type='billing'", 98 | ['country_count' => 'COUNT(country_id)', 'country_id'] 99 | )->group('country_id')->order('country_count DESC'); 100 | if ($size) { 101 | $collection->setPageSize($size); 102 | } 103 | 104 | foreach ($collection as $item) { 105 | $data[$item->getCountryId()] = ['count' => $item->getCountryCount()]; 106 | } 107 | 108 | return $data; 109 | } 110 | 111 | /** 112 | * @return array 113 | * @throws LocalizedException 114 | * @throws Exception 115 | */ 116 | public function getCollection() 117 | { 118 | $collection = []; 119 | $date = $this->_helperData->getDateRange(); 120 | $data = $this->getDataByDateRange($date[0], $date[1], 5); 121 | $compareData = $this->getDataByDateRange($date[2], $date[3]); 122 | foreach ($data as $key => $item) { 123 | if (isset($compareData[$key]) && $compareData[$key] > 0) { 124 | $rate = ($item['count'] - $compareData[$key]['count']) / $compareData[$key]['count']; 125 | } else { 126 | $rate = 1; 127 | } 128 | $collection[] = [ 129 | 'country' => $this->getCountryNameById($key), 130 | 'count' => $item['count'], 131 | 'rate' => round($rate * 100, 2) 132 | ]; 133 | } 134 | 135 | return $collection; 136 | } 137 | 138 | /** 139 | * @param $countryId 140 | * 141 | * @return string 142 | */ 143 | private function getCountryNameById($countryId) 144 | { 145 | $country = $this->_countryFactory->create()->loadByCode($countryId); 146 | 147 | return $country->getName(); 148 | } 149 | 150 | /** 151 | * @return Phrase|string 152 | */ 153 | public function getTitle() 154 | { 155 | return __('Sales By Location'); 156 | } 157 | 158 | /** 159 | * @return bool 160 | */ 161 | public function canShowDetail() 162 | { 163 | return true; 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /Block/Dashboard/Shipping.php: -------------------------------------------------------------------------------- 1 | _helperData->getDateRange(); 52 | $totals = $this->_helperData->getTotalsByDateRange($date[0], $date[1]); 53 | 54 | return $this->getBaseCurrency()->format($totals->getShipping() ?: 0, [], $includeContainer); 55 | } 56 | 57 | /** 58 | * @return float|int 59 | * @throws LocalizedException 60 | * @throws Exception 61 | */ 62 | public function getRate() 63 | { 64 | $dates = $this->_helperData->getDateRange(); 65 | $totals = $this->_helperData->getTotalsByDateRange($dates[0], $dates[1]); 66 | $compareTotals = $this->_helperData->getTotalsByDateRange($dates[2], $dates[3]); 67 | if ((int) $totals->getShipping() === 0 && (int) $compareTotals->getShipping() === 0) { 68 | return 0; 69 | } 70 | if ((int) $compareTotals->getShipping() === 0) { 71 | return 100; 72 | } 73 | if ((int) $totals->getShipping() === 0) { 74 | return -100; 75 | } 76 | 77 | return round( 78 | (($totals->getShipping() - $compareTotals->getShipping()) / $compareTotals->getShipping()) * 100, 79 | 2 80 | ); 81 | } 82 | 83 | /** 84 | * @param $date 85 | * @param null $endDate 86 | * 87 | * @return float|int 88 | * @throws LocalizedException 89 | */ 90 | protected function getDataByDate($date, $endDate = null) 91 | { 92 | $totals = $this->_helperData->getTotalsByDateRange($date, $endDate); 93 | 94 | return round($totals->getShipping() ?: 0, 2); 95 | } 96 | 97 | /** 98 | * @return string 99 | * @throws NoSuchEntityException 100 | */ 101 | protected function getYUnit() 102 | { 103 | return $this->getBasePriceFormat(); 104 | } 105 | 106 | /** 107 | * @return Phrase|string 108 | */ 109 | public function getTitle() 110 | { 111 | return __('Shipping'); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /Block/Dashboard/Tax.php: -------------------------------------------------------------------------------- 1 | _helperData->getDateRange(); 53 | $totals = $this->_helperData->getTotalsByDateRange($date[0], $date[1]); 54 | 55 | foreach ($totals->getItems() as $item) { 56 | $total += $item->getTaxBaseAmountSum(); 57 | } 58 | 59 | return $this->getBaseCurrency()->format($total, [], $includeContainer); 60 | } 61 | 62 | /** 63 | * @return float|int 64 | * @throws LocalizedException 65 | * @throws Exception 66 | */ 67 | public function getRate() 68 | { 69 | $dates = $this->_helperData->getDateRange(); 70 | $totals = $this->_helperData->getTotalsByDateRange($dates[0], $dates[1])->getFirstItem(); 71 | $compareTotals = $this->_helperData->getTotalsByDateRange($dates[2], $dates[3])->getFirstItem(); 72 | if ((int) $totals->getTaxBaseAmountSum() === 0 && (int) $compareTotals->getTaxBaseAmountSum() === 0) { 73 | return 0; 74 | } 75 | if ((int) $compareTotals->getTaxBaseAmountSum() === 0) { 76 | return 100; 77 | } 78 | if ((int) $totals->getTaxBaseAmountSum() === 0) { 79 | return -100; 80 | } 81 | 82 | return round((($totals->getTaxBaseAmountSum() - $compareTotals->getTaxBaseAmountSum()) / $compareTotals->getTaxBaseAmountSum()) * 100, 2); 83 | } 84 | 85 | /** 86 | * @param $date 87 | * @param null $endDate 88 | * 89 | * @return float|int 90 | * @throws LocalizedException 91 | */ 92 | protected function getDataByDate($date, $endDate = null) 93 | { 94 | $totals = $this->_helperData->getTotalsByDateRange($date, $endDate)->getFirstItem(); 95 | 96 | return round($totals->getTaxBaseAmountSum() ?: 0, 2); 97 | } 98 | 99 | /** 100 | * @return string 101 | * @throws NoSuchEntityException 102 | */ 103 | protected function getYUnit() 104 | { 105 | return $this->getBasePriceFormat(); 106 | } 107 | 108 | /** 109 | * @return Phrase|string 110 | */ 111 | public function getTitle() 112 | { 113 | return __('Tax'); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Block/Dashboard/TopSearches.php: -------------------------------------------------------------------------------- 1 | _helperData->getDateRange(); 52 | $totals = $this->_helperData->getTotalsByDateRange($date[0], $date[1]); 53 | 54 | return $this->getBaseCurrency()->format($totals->getRevenue() ?: 0, [], $includeContainer); 55 | } 56 | 57 | /** 58 | * @return float|int 59 | * @throws LocalizedException 60 | * @throws Exception 61 | */ 62 | public function getRate() 63 | { 64 | $dates = $this->_helperData->getDateRange(); 65 | $totals = $this->_helperData->getTotalsByDateRange($dates[0], $dates[1]); 66 | $compareTotals = $this->_helperData->getTotalsByDateRange($dates[2], $dates[3]); 67 | if ((int) $totals->getRevenue() === 0 && (int) $compareTotals->getRevenue() === 0) { 68 | return 0; 69 | } 70 | if ((int) $compareTotals->getRevenue() === 0) { 71 | return 100; 72 | } 73 | if ((int) $totals->getRevenue() === 0) { 74 | return -100; 75 | } 76 | 77 | return round((($totals->getRevenue() - $compareTotals->getRevenue()) / $compareTotals->getRevenue()) * 100, 2); 78 | } 79 | 80 | /** 81 | * @param $date 82 | * @param null $endDate 83 | * 84 | * @return float|int 85 | * @throws LocalizedException 86 | */ 87 | protected function getDataByDate($date, $endDate = null) 88 | { 89 | $totals = $this->_helperData->getTotalsByDateRange($date, $endDate); 90 | 91 | return round($totals->getRevenue() ?: 0, 2); 92 | } 93 | 94 | /** 95 | * @return string 96 | * @throws NoSuchEntityException 97 | */ 98 | protected function getYUnit() 99 | { 100 | return $this->getBasePriceFormat(); 101 | } 102 | 103 | /** 104 | * @return Phrase|string 105 | */ 106 | public function getTitle() 107 | { 108 | return __('Totals'); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Block/Menu.php: -------------------------------------------------------------------------------- 1 | customerGroup = $customerGroup; 82 | $this->orderStatus = $orderStatus; 83 | $this->helperData = $helperData; 84 | $this->girdName = $girdName; 85 | $this->menuUrls = $menuUrls; 86 | 87 | parent::__construct($context, $data); 88 | } 89 | 90 | /** 91 | * @return array 92 | */ 93 | public function getCustomerGroup() 94 | { 95 | return $this->customerGroup->toOptionArray(); 96 | } 97 | 98 | /** 99 | * @return array 100 | * @throws Exception 101 | */ 102 | public function getDateRange() 103 | { 104 | $dateRange = $this->helperData->getDateRange(); 105 | if ($startDate = $this->getRequest()->getParam('startDate')) { 106 | $dateRange[0] = $startDate; 107 | } 108 | if ($endDate = $this->getRequest()->getParam('endDate')) { 109 | $dateRange[1] = $endDate; 110 | } 111 | 112 | return $dateRange; 113 | } 114 | 115 | /** 116 | * @return string 117 | */ 118 | public function getGridName() 119 | { 120 | $fullActionName = $this->getRequest()->getFullActionName(); 121 | 122 | return isset($this->girdName[$fullActionName]) ? $this->girdName[$fullActionName] : ''; 123 | } 124 | 125 | /** 126 | * @return array 127 | */ 128 | public function getMenuUrls() 129 | { 130 | $urls = []; 131 | foreach ($this->menuUrls as $path => $label) { 132 | $urls[] = [ 133 | 'label' => __($label), 134 | 'path' => $this->geturl($path) 135 | ]; 136 | } 137 | 138 | return $urls; 139 | } 140 | 141 | /** 142 | * @return array 143 | */ 144 | public function getOrderStatusList() 145 | { 146 | $statuses = $this->orderStatus->toOptionArray(); 147 | array_shift($statuses); 148 | 149 | return $statuses; 150 | } 151 | 152 | /** 153 | * @return string 154 | * @throws Exception 155 | */ 156 | public function getDate() 157 | { 158 | return Data::jsonEncode($this->helperData->getDateRange()); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /Block/Store/Switcher.php: -------------------------------------------------------------------------------- 1 | _helperData = $helperData; 58 | } 59 | 60 | /** 61 | * @return string 62 | */ 63 | protected function _toHtml() 64 | { 65 | if (!$this->canShow()) { 66 | return ''; 67 | } 68 | 69 | return parent::_toHtml(); // TODO: Change the autogenerated stub 70 | } 71 | 72 | /** 73 | * @return bool 74 | */ 75 | private function canShow() 76 | { 77 | return $this->_helperData->getConfigGeneral('shownof') && !$this->_helperData->isEnabled(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | CHANGELOG: https://www.mageplaza.com/releases/reports/ -------------------------------------------------------------------------------- /Controller/Adminhtml/Cards/LoadCard.php: -------------------------------------------------------------------------------- 1 | _authSession = $authSession; 63 | $this->_cardsManageFactory = $cardsManageFactory; 64 | 65 | parent::__construct($context); 66 | } 67 | 68 | /** 69 | * @return ResponseInterface|ResultInterface 70 | * @throws Exception 71 | */ 72 | public function execute() 73 | { 74 | $id = $this->getRequest()->getParam('card_id'); 75 | $layout = $this->_view->getLayout(); 76 | $data = []; 77 | 78 | $cardsManager = $this->_cardsManageFactory->create(); 79 | if ($id && isset($cardsManager[$id]) && $this->getRequest()->isAjax()) { 80 | try { 81 | /** @var Card $block */ 82 | $block = $layout->createBlock(Card::class); 83 | $block->setCard($cardsManager[$id]); 84 | $data['html'] = $block->toHtml(); 85 | } catch (Exception $exception) { 86 | $data['message'] = $exception->getMessage(); 87 | } 88 | } 89 | if (empty($data)) { 90 | $data['message'] = __('Can not be load Card'); 91 | } 92 | 93 | return $this->getResponse()->representJson(Data::jsonEncode($data)); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Controller/Adminhtml/Cards/SavePosition.php: -------------------------------------------------------------------------------- 1 | _authSession = $authSession; 62 | $this->_cardsManageFactory = $cardsManageFactory; 63 | 64 | parent::__construct($context); 65 | } 66 | 67 | /** 68 | * @return ResponseInterface|ResultInterface|void 69 | * @throws Exception 70 | */ 71 | public function execute() 72 | { 73 | $items = $this->getRequest()->getParam('items'); 74 | if ($items && $this->getRequest()->isAjax()) { 75 | $userId = $this->_authSession->getUser()->getId(); 76 | $config = $this->_cardsManageFactory->getCurrentConfig(); 77 | $data = $config->getId() 78 | ? $config->getConfig() 79 | : $this->_cardsManageFactory->getDefaultConfig()->getConfig(); 80 | foreach ($items as $id => $item) { 81 | $data[$id]['x'] = $item['x']; 82 | $data[$id]['y'] = $item['y']; 83 | $data[$id]['width'] = $item['width']; 84 | $data[$id]['height'] = $item['height']; 85 | $data[$id]['visible'] = isset($item['visible']) ? $item['visible'] : 1; 86 | } 87 | $data = Data::jsonEncode($data); 88 | if ($config->getId()) { 89 | $config->setConfig($data)->save(); 90 | } else { 91 | $config->setData([ 92 | 'namespace' => 'mageplaza_reports_cards', 93 | 'user_id' => $userId, 94 | 'identifier' => 'current', 95 | 'config' => $data 96 | ])->save(); 97 | } 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Controller/Adminhtml/Dashboard/Index.php: -------------------------------------------------------------------------------- 1 | resultPageFactory = $resultPageFactory; 66 | $this->_jsonHelper = $jsonHelper; 67 | 68 | parent::__construct($context); 69 | } 70 | 71 | /** 72 | * @return Page|ResponseInterface|ResultInterface 73 | */ 74 | public function execute() 75 | { 76 | /** @var Page $resultPage */ 77 | $resultPage = $this->resultPageFactory->create(); 78 | $resultPage->setActiveMenu('Maqeplaza_Reports::dashboard'); 79 | $resultPage->addBreadcrumb(__('Dashboard'), __('Dashboard')); 80 | $resultPage->getConfig()->getTitle()->prepend(__('Dashboard')); 81 | if ($this->getRequest()->isAjax()) { 82 | $dashBoard = $resultPage->getLayout()->getBlock('ar_dashboard'); 83 | $result = ['dashboard' => $dashBoard->toHtml()]; 84 | 85 | return $this->getResponse()->representJson($this->_jsonHelper->jsonEncode($result)); 86 | } 87 | 88 | return $resultPage; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Controller/Adminhtml/Dashboard/RefreshStatistics.php: -------------------------------------------------------------------------------- 1 | _storageWriter = $storageWriter; 64 | $this->_cache = $typeList; 65 | 66 | parent::__construct($context); 67 | } 68 | 69 | /** 70 | * @return ResponseInterface|Redirect|ResultInterface|null 71 | */ 72 | public function execute() 73 | { 74 | if ($this->getRequest()->isAjax()) { 75 | if ($this->getRequest()->getParam('switchToNew')) { 76 | $this->saveConfig('mageplaza_reports/general/enabled', 1); 77 | $this->getResponse()->representJson( 78 | Data::jsonEncode($this->getUrl('admin/dashboard/index')) 79 | ); 80 | } 81 | if ($this->getRequest()->getParam('firstTimeInstall')) { 82 | $this->saveConfig('mageplaza_reports/general/first_time_install', 0); 83 | 84 | return null; 85 | } 86 | $this->saveConfig('mageplaza_reports/general/shownof', 0); 87 | 88 | return null; 89 | } 90 | $this->saveConfig('mageplaza_reports/general/enabled', 1); 91 | 92 | return $this->resultRedirectFactory->create()->setPath('admin/dashboard/index'); 93 | } 94 | 95 | /** 96 | * @param $code 97 | * @param $value 98 | */ 99 | private function saveConfig($code, $value) 100 | { 101 | $this->_storageWriter->save( 102 | $code, 103 | $value, 104 | ScopeConfigInterface::SCOPE_TYPE_DEFAULT, 105 | Store::DEFAULT_STORE_ID 106 | ); 107 | $this->_cache->cleanType('config'); 108 | $this->_cache->cleanType('full_page'); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Controller/Adminhtml/Details/StoreFilter.php: -------------------------------------------------------------------------------- 1 | resultPageFactory = $resultPageFactory; 52 | 53 | parent::__construct($context); 54 | } 55 | 56 | /** 57 | * @return Page 58 | */ 59 | public function execute() 60 | { 61 | $resultPage = $this->resultPageFactory->create(); 62 | if ($this->getRequest()->isAjax()) { 63 | $storeHtml = $resultPage->getLayout() 64 | ->createBlock(Switcher::class) 65 | ->setSwitchWebsites(0) 66 | ->setSwitchStoreGroups(0) 67 | ->setSwitchStoreViews(1) 68 | ->setUseConfirm(0) 69 | ->setDefaultSelectionName(__('All Websites')) 70 | ->toHtml(); 71 | 72 | return $this->getResponse()->representJson( 73 | Data::jsonEncode( 74 | ['store' => $storeHtml] 75 | ) 76 | ); 77 | } 78 | 79 | return $resultPage; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Controller/Cards/LoadCard.php: -------------------------------------------------------------------------------- 1 | _authSession = $authSession; 63 | $this->_cardsManageFactory = $cardsManageFactory; 64 | 65 | parent::__construct($context); 66 | } 67 | 68 | /** 69 | * @return ResponseInterface|ResultInterface 70 | * @throws Exception 71 | */ 72 | public function execute() 73 | { 74 | $id = $this->getRequest()->getParam('card_id'); 75 | $layout = $this->_view->getLayout(); 76 | $data = []; 77 | 78 | $cardsManager = $this->_cardsManageFactory->create(); 79 | if ($id && isset($cardsManager[$id]) && $this->getRequest()->isAjax()) { 80 | try { 81 | /** @var Card $block */ 82 | $block = $layout->createBlock(Card::class); 83 | $block->setCard($cardsManager[$id]); 84 | $data['html'] = $block->toHtml(); 85 | } catch (Exception $exception) { 86 | $data['message'] = $exception->getMessage(); 87 | } 88 | } 89 | if (empty($data)) { 90 | $data['message'] = __('Can not be load Card'); 91 | } 92 | 93 | return $this->getResponse()->representJson(Data::jsonEncode($data)); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Controller/Cards/SavePosition.php: -------------------------------------------------------------------------------- 1 | _authSession = $authSession; 62 | $this->_cardsManageFactory = $cardsManageFactory; 63 | 64 | parent::__construct($context); 65 | } 66 | 67 | /** 68 | * @return ResponseInterface|ResultInterface|void 69 | * @throws Exception 70 | */ 71 | public function execute() 72 | { 73 | $items = $this->getRequest()->getParam('items'); 74 | if ($items && $this->getRequest()->isAjax()) { 75 | $config = $this->_cardsManageFactory->getCurrentConfig(); 76 | $data = $config->getId() 77 | ? $config->getConfig() 78 | : $this->_cardsManageFactory->getDefaultConfig()->getConfig(); 79 | foreach ($items as $id => $item) { 80 | $data[$id]['x'] = $item['x']; 81 | $data[$id]['y'] = $item['y']; 82 | $data[$id]['width'] = $item['width']; 83 | $data[$id]['height'] = $item['height']; 84 | $data[$id]['visible'] = isset($item['visible']) ? $item['visible'] : 1; 85 | } 86 | $data = Data::jsonEncode($data); 87 | if ($config->getId()) { 88 | $config->setConfig($data)->save(); 89 | } else { 90 | $config->setData([ 91 | 'namespace' => 'mageplaza_reports_cards_mobile', 92 | 'user_id' => '1', 93 | 'identifier' => 'current', 94 | 'config' => $data 95 | ])->save(); 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /Controller/Dashboard/Index.php: -------------------------------------------------------------------------------- 1 | resultPageFactory = $resultPageFactory; 70 | $this->resultForwardFactory = $resultForwardFactory; 71 | $this->helperData = $helperData; 72 | 73 | parent::__construct($context); 74 | } 75 | 76 | /** 77 | * @return ResponseInterface|Forward|ResultInterface|Page 78 | */ 79 | public function execute() 80 | { 81 | $resultPage = $this->resultPageFactory->create(); 82 | 83 | $accessKey = $this->getRequest()->getParam('accessKey'); 84 | $accessKeyConfig = $this->helperData->getConfigMobileAccessKey(); 85 | if ($accessKey !== $accessKeyConfig) { 86 | return $this->_redirect('noroute'); 87 | } 88 | if ($this->getRequest()->isAjax()) { 89 | $dashBoard = $resultPage->getLayout()->getBlock('ar_dashboard'); 90 | $result = ['dashboard' => $dashBoard->toHtml()]; 91 | 92 | return $this->getResponse()->representJson(Data::jsonEncode($result)); 93 | } 94 | 95 | return $resultPage; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2016-present Mageplaza Co. Ltd. 2 | 3 | This License is entered by Mageplaza to govern the usage or redistribution of Mageplaza software. This is a legal agreement between you (either an individual or a single entity) and Mageplaza for Mageplaza software product(s) which may include extensions, templates and services. 4 | 5 | By purchasing, installing, or otherwise using Mageplaza products, you acknowledge that you have read this License and agree to be bound by the terms of this Agreement. If you do not agree to the terms of this License, do not install or use Mageplaza products. 6 | 7 | The Agreement becomes effective at the moment when you acquire software from our site or receive it through email or on data medium or by any other means. Mageplaza reserves the right to make reasonable changes to the terms of this license agreement and impose its clauses at any given time. 8 | 9 | 1. GRANT OF LICENSE: By purchasing a product of Mageplaza: 10 | 11 | 1. Customer will receive source code open 100%. 12 | 13 | 2. Customer will obtain a License Certificate which will remain valid until the Customer stops using the Product or until Mageplaza terminates this License because of Customer’s failure to comply with any of its Terms and Conditions. Each License Certificate includes a license serial which is valid for one live Magento installation only and unlimited test Magento installations. 14 | 15 | 3. You are allowed to customize our products to fit with your using purpose. 16 | 17 | 4. DESCRIPTION OF OTHER RIGHTS AND LIMITATIONS 18 | 19 | 5. Installation and Use 20 | 21 | 6. For each new Software installation, you are obliged to purchase a separate License. You are not permitted to use any part of the code in whole or part in any other software or product or website. You are legally bound to preserve the copyright information intact including the text/link at bottom. 22 | 23 | 2. Distribution: You are not allowed to distribute Mageplaza software to third parties. Any distribution without our permission, including non commercial distribution is considered as violation of this Agreement and entails liability, according to the current law. You may not place the Software onto a server that allows access to the Software via a public network or the Internet for distribution purposes. 24 | 25 | 3. Rental: You may not give, sell, sub-license, rent, lease or lend any portion of the Software to anyone. 26 | 27 | 4. Compliance with Applicable Laws: You must comply with all applicable laws regarding use of software products. Mageplaza software and a portion of it are protected by copyright laws and international copyright treaties, as well as other intellectual property laws and treaties. Accordingly, customer is required to treat the software like any other copyrighted material. Any activity violating copyright law will be prosecuted according to the current law. We retain the right to revoke the license of any user holding an invalid license. 28 | 29 | 5. TERMINATION: Without prejudice to any other rights, Mageplaza may terminate this License at any time if you fail to comply with the terms and conditions of this License. In such event, it constitutes a breach of the agreement, and your license to use the program is revoked and you must destroy all copies of Mageplaza products in your possession. After being notified of termination of your license, if you continue to use Mageplaza software, you hereby agree to accept an injunction to prevent you from its further use and to pay all costs (including but not limited to reasonable attorney fees) to enforce our revocation of your license and any damages suffered by us because of your misuse of the Software. We are not bound to return you the amount spent for purchase of the Software for the termination of this License. 30 | 31 | 6. LIMITATION OF LIABILITY: In no event shall Mageplaza be liable for any damages (including, without limitation, lost profits, business interruption, or lost information) rising out of ‘Authorized Users’ use of or inability to use the Mageplaza products, even if Mageplaza has been advised of the possibility of such damages. In no event will Mageplaza be liable for prosecution arising from use of the Software against law or for any illegal use. 32 | 33 | The latest License: https://www.mageplaza.com/LICENSE.txt -------------------------------------------------------------------------------- /Model/Api/Data/Card.php: -------------------------------------------------------------------------------- 1 | name; 24 | } 25 | 26 | /** 27 | * @param string $name 28 | * 29 | * @return mixed|void 30 | */ 31 | public function setName($name) 32 | { 33 | $this->name = $name; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Model/ResourceModel/Viewed/Collection.php: -------------------------------------------------------------------------------- 1 | _init(\Magento\Reports\Model\Product\Index\Viewed::class, Viewed::class); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Plugin/BackendLogoUrl.php: -------------------------------------------------------------------------------- 1 | _helperData = $helperData; 55 | $this->_backendUrl = $backendUrl; 56 | } 57 | 58 | /** 59 | * @param BackendHelper $data 60 | * @param $result 61 | * 62 | * @return string 63 | * @SuppressWarnings(Unused) 64 | */ 65 | public function afterGetHomePageUrl(BackendHelper $data, $result) 66 | { 67 | if ($this->_helperData->isEnabledDashboard()) { 68 | $result = $this->_backendUrl->getRouteUrl('mpreports/dashboard'); 69 | } 70 | 71 | return $result; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Plugin/Config.php: -------------------------------------------------------------------------------- 1 | helper = $helper; 46 | } 47 | 48 | /** 49 | * @param BackendConfig $subject 50 | * @param $result 51 | * 52 | * @return mixed 53 | */ 54 | public function afterGetRouteByFrontName(BackendConfig $subject, $result) 55 | { 56 | if (!$result && $this->helper->versionCompare('2.2.8', '=')) { 57 | return 'adminhtml'; 58 | } 59 | 60 | return $result; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Plugin/Framework/Reflection/DataObjectProcessor.php: -------------------------------------------------------------------------------- 1 | methodsMapProcessor = $methodsMapProcessor; 45 | $this->fieldNamer = $fieldNamer; 46 | $this->helperData = $helperData; 47 | } 48 | 49 | /** 50 | * @param \Magento\Framework\Reflection\DataObjectProcessor $dataObjectProcessor 51 | * @param $dataObject 52 | * @param $dataObjectType 53 | * @param $outputData 54 | * 55 | * @return mixed 56 | * @throws ReflectionException 57 | */ 58 | public function afterBuildOutputDataArray( 59 | \Magento\Framework\Reflection\DataObjectProcessor $dataObjectProcessor, 60 | $outputData, 61 | $dataObject, 62 | $dataObjectType 63 | ) { 64 | if (!$this->helperData->isEnabled()) { 65 | return $outputData; 66 | } 67 | $methods = $this->methodsMapProcessor->getMethodsMap($dataObjectType); 68 | if (strpos($dataObjectType, CardInterface::class) === false) { 69 | return $outputData; 70 | } 71 | foreach (array_keys($methods) as $methodName) { 72 | if (!$this->methodsMapProcessor->isMethodValidForDataField($dataObjectType, $methodName)) { 73 | continue; 74 | } 75 | $key = $this->fieldNamer->getFieldNameForMethodName($methodName); 76 | $value = $dataObject->{$methodName}(); 77 | if ($key === 'data') { 78 | $outputData[$key] = $value; 79 | } 80 | } 81 | 82 | return $outputData; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Plugin/Model/StartupPageUrl.php: -------------------------------------------------------------------------------- 1 | resultRedirectFactory = $resultRedirectFactory; 55 | $this->_helperData = $helperData; 56 | } 57 | 58 | /** 59 | * @param Url $url 60 | * @param $result 61 | * 62 | * @return string 63 | * @SuppressWarnings(Unused) 64 | */ 65 | public function afterGetStartupPageUrl(Url $url, $result) 66 | { 67 | if ($this->_helperData->isEnabledDashboard()) { 68 | $result = 'mpreports/dashboard'; 69 | } 70 | 71 | return $result; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Plugin/MoveMenu.php: -------------------------------------------------------------------------------- 1 | helper = $helper; 46 | } 47 | 48 | /** 49 | * @param AbstractCommand $subject 50 | * @param $itemParams 51 | * 52 | * @return mixed 53 | * @SuppressWarnings(Unused) 54 | */ 55 | public function afterExecute(AbstractCommand $subject, $itemParams) 56 | { 57 | if ($itemParams['id'] === 'Mageplaza_Reports::dashboard' && !$this->helper->isEnabledDashboard()) { 58 | $itemParams['removed'] = true; 59 | } 60 | 61 | return $itemParams; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /USER-GUIDE.md: -------------------------------------------------------------------------------- 1 | # Reports User Guide 2 | 3 | ## Documentation 4 | - Installation guide: https://www.mageplaza.com/install-magento-2-extension/ 5 | - User guide: https://docs.mageplaza.com/reports/index.html 6 | - Download from our Live site: https://www.mageplaza.com/magento-2-reports-extension/ 7 | - Get Support: https://github.com/mageplaza/magento-2-reports/issues 8 | - Contribute on Github: https://github.com/mageplaza/magento-2-reports 9 | - Changelog: https://www.mageplaza.com/releases/reports/ 10 | - License https://www.mageplaza.com/LICENSE.txt 11 | 12 | 13 | ## How to install 14 | 15 | ### Method 1: Install ready-to-paste package 16 | 17 | - Download the latest version at [Mageplaza Reports for Magento 2](https://www.mageplaza.com/magento-2-reports-extension/) 18 | - [Installation guide](https://www.mageplaza.com/install-magento-2-extension/) 19 | 20 | ### Method 2: Install via composer [Recommend] 21 | 22 | Run the following command in Magento 2 root folder 23 | 24 | ``` 25 | composer require mageplaza/module-reports 26 | php bin/magento setup:upgrade 27 | php bin/magento setup:static-content:deploy 28 | ``` 29 | 30 | ## FAQs 31 | 32 | #### Q: I got error: `Mageplaza_Core has been already defined` 33 | A: Read solution: https://github.com/mageplaza/module-core/issues/3 34 | 35 | #### Q: My site is down 36 | A: Please follow this guide: https://www.mageplaza.com/blog/magento-site-down.html 37 | -------------------------------------------------------------------------------- /UserGuide.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mageplaza/magento-2-reports/ea6c8ebba644a1492a3dc623d6117a756406cefd/UserGuide.pdf -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mageplaza/module-reports", 3 | "description": "Magento 2 Reports Extension", 4 | "require": { 5 | "mageplaza/module-core": "^1.5.13" 6 | }, 7 | "type": "magento2-module", 8 | "version": "4.1.6", 9 | "license": "proprietary", 10 | "authors": [ 11 | { 12 | "name": "Mageplaza", 13 | "email": "support@mageplaza.com", 14 | "homepage": "https://www.mageplaza.com", 15 | "role": "Technical Support" 16 | } 17 | ], 18 | "autoload": { 19 | "files": [ 20 | "registration.php" 21 | ], 22 | "psr-4": { 23 | "Mageplaza\\Reports\\": "" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /etc/acl.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /etc/adminhtml/di.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /etc/adminhtml/menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /etc/adminhtml/routes.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /etc/adminhtml/system.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 |
26 | separator-top 27 | 28 | mageplaza 29 | Mageplaza_Reports::configuration 30 | 31 | 32 | 33 | 34 | Magento\Config\Model\Config\Source\Yesno 35 | Yes to enable this module.
36 | 1. It helps to reduce abandonment cart with One Step Checkout.
37 | 2. Magento stores see upwards of 30% revenue 💰 with AVADA. Learn more]]>
38 |
39 | 40 | 41 | Magento\Config\Model\Config\Source\Yesno 42 | Mageplaza\Core\Model\Config\Backend\Menu 43 | 44 | 45 | 46 | Magento\Config\Model\Config\Source\Yesno 47 | 48 | 49 | 50 | Magento\Config\Model\Config\Source\Yesno 51 | 52 |
53 | 54 | 55 | 56 | 57 | You must save config to apply change. 58 | 59 | 60 | Mageplaza\Reports\Block\Adminhtml\System\Config\LinkBuilder 61 | 62 | 63 |
64 |
65 |
66 | -------------------------------------------------------------------------------- /etc/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | 26 | 27 | magento/reports 28 | 29 | 30 | 0 31 | 1 32 | 1 33 | 1 34 | 1 35 | 1 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /etc/di.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | 26 | 27 | Mageplaza\Reports\Block\Dashboard\LastOrders 28 | Mageplaza\Reports\Block\Dashboard\LastSearches 29 | Mageplaza\Reports\Block\Dashboard\TopSearches 30 | Mageplaza\Reports\Block\Dashboard\Orders 31 | Mageplaza\Reports\Block\Dashboard\AverageOrderValue 32 | Mageplaza\Reports\Block\Dashboard\SalesByLocation 33 | Mageplaza\Reports\Block\Dashboard\RepeatCustomerRate 34 | Mageplaza\Reports\Block\Dashboard\TotalSales 35 | Mageplaza\Reports\Block\Dashboard\Bestsellers 36 | Mageplaza\Reports\Block\Dashboard\MostViewedProducts 37 | Mageplaza\Reports\Block\Dashboard\NewCustomers 38 | Mageplaza\Reports\Block\Dashboard\Customers 39 | Mageplaza\Reports\Block\Dashboard\Tax 40 | Mageplaza\Reports\Block\Dashboard\Shipping 41 | Mageplaza\Reports\Block\Dashboard\LifetimeSales 42 | Mageplaza\Reports\Block\Dashboard\AverageOrder 43 | Mageplaza\Reports\Block\Dashboard\ConversionFunnel 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /etc/frontend/routes.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /etc/module.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /etc/webapi.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /i18n/en_US.csv: -------------------------------------------------------------------------------- 1 | "Average Order","Average Order" 2 | "Average Order Value","Average Order Value" 3 | Bestsellers,Bestsellers 4 | "Conversion Funnel","Conversion Funnel" 5 | Customers,Customers 6 | "Last Order","Last Order" 7 | "Last Search Terms","Last Search Terms" 8 | "Lifetime Sales","Lifetime Sales" 9 | "Most Viewed Products","Most Viewed Products" 10 | "New Customers","New Customers" 11 | Orders,Orders 12 | first,first 13 | repeat,repeat 14 | "Repeat Customer Rate","Repeat Customer Rate" 15 | "Sales By Location","Sales By Location" 16 | Shipping,Shipping 17 | Tax,Tax 18 | "Top Search Terms","Top Search Terms" 19 | Totals,Totals 20 | "Can not be load Card","Can not be load Card" 21 | Dashboard,Dashboard 22 | "All Websites","All Websites" 23 | "compare with","compare with" 24 | Card,Card 25 | "Please upgrade to %1 version to see more detail","Please upgrade to %1 version to see more detail" 26 | "View Details","View Details" 27 | "Something went wrong while loading %1 card","Something went wrong while loading %1 card" 28 | "Chart is disabled. To enable the chart, click here","Chart is disabled. To enable the chart, click here" 29 | "Product Viewed: %1","Product Viewed: %1" 30 | "Add To Cart: %1","Add To Cart: %1" 31 | "Ordered: %1","Ordered: %1" 32 | "We couldn't find any records.","We couldn't find any records." 33 | Period,Period 34 | Day,Day 35 | Week,Week 36 | Month,Month 37 | Year,Year 38 | "Order Status","Order Status" 39 | --Select--,--Select-- 40 | "Date Used","Date Used" 41 | "Created At","Created At" 42 | "Updated At","Updated At" 43 | "Let’s experience your new dashboard","Let’s experience your new dashboard" 44 | "Hello there! Better reports are available","Hello there! Better reports are available" 45 | Scope:,Scope: 46 | "Stores Configuration","Stores Configuration" 47 | "Mageplaza Advanced Reporting","Mageplaza Advanced Reporting" 48 | "The better reports provides ecommerce insights across performance metrics, product sales, customers, and cost of goods sold. Instantly measure metrics like customer lifetime value, gross margin, net profit, and more to build and automate high-quality ecommerce reports.","The better reports provides ecommerce insights across performance metrics, product sales, customers, and cost of goods sold. Instantly measure metrics like customer lifetime value, gross margin, net profit, and more to build and automate high-quality ecommerce reports." 49 | "Try to use new Dashboard","Try to use new Dashboard" 50 | "Change Key","Change Key" 51 | "Generate Url","Generate Url" 52 | Copied!,Copied! 53 | Continue,Continue 54 | "Please confirm scope switching. All data that hasn't been saved will be lost.","Please confirm scope switching. All data that hasn't been saved will be lost." 55 | "Mageplaza Reports Dashboard","Mageplaza Reports Dashboard" 56 | Reports,Reports 57 | "General Configuration","General Configuration" 58 | "Module Enable","Module Enable" 59 | "Enable Dashboard","Enable Dashboard" 60 | "Enable Chart","Enable Chart" 61 | "Enable Comparison","Enable Comparison" 62 | "Mobile Dashboard","Mobile Dashboard" 63 | "Access Key","Access Key" 64 | "You must save config to apply change.","You must save config to apply change." 65 | -------------------------------------------------------------------------------- /registration.php: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /view/adminhtml/layout/adminhtml_system_config_edit.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /view/adminhtml/layout/mpreports_dashboard_index.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /view/adminhtml/layout/store_switcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | complex 33 | 34 | 35 | 36 | 37 | 38 | 39 | 0 40 | 0 41 | 1 42 | 0 43 | All Websites 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /view/adminhtml/templates/dashboard.phtml: -------------------------------------------------------------------------------- 1 | getCards(); 26 | 27 | ?> 28 |
29 |
30 |
true]) ?>","isCompare":isCompare() ?>,"date":getDate() ?>}}'> 32 |
33 | 34 | 35 | 36 |
37 | isCompare()): ?> 38 |
escapeHtml(__('compare with')) ?>
39 |
40 | 41 | 42 | 43 |
44 | 45 |
46 |
47 |
48 |
50 | 53 |
54 |
55 |
56 | 57 |
58 | getVisible() ? 'checked' : '' ?> 60 | id="card-getCardId() ?>" 61 | data-card-id="getCardId() ?>"> 62 | 67 |
68 | 69 |
70 |
71 |
72 | getChildHtml('mpreports_additional_header') ?> 73 |
74 |
75 |
77 | 78 | getVisible()): ?> 79 | getChildBlock('mp_card')->setCard($card)->toHtml() ?> 80 | 81 | 82 | 103 |
104 |
105 |
106 | -------------------------------------------------------------------------------- /view/adminhtml/templates/dashboard/card.phtml: -------------------------------------------------------------------------------- 1 | getCard(); 31 | ?> 32 |
40 |
41 |
42 |
43 |
44 | getTitle()) : ?> 45 |
escapeHtml($title) ?>
46 | 47 | canShowDetail() && $card->getDetailUrl()) : ?> 48 | 53 | 54 |
55 | getRequest()->isAjax() 56 | && $block->getRequest()->getFullActionName() === 'mpreports_cards_loadcard') : ?> 57 | getTotal(); 59 | if ($totalLabel) : ?> 60 |
61 | 63 | getRate(); 65 | if (($rate !== '') && $block->isCompare()) : ?> 66 |
68 | 70 |
71 | 72 |
73 | 74 | 75 |
76 | getRequest()->isAjax() 77 | && $block->getRequest()->getFullActionName() === 'mpreports_cards_loadcard') : ?> 78 | getContentHtml() ?> 79 | 80 |
81 | Loading... 83 |
84 | 85 | getRequest()->getFullActionName() !== 'mpreports_cards_loadcard') : ?> 86 | 132 | 133 |
134 |
135 |
136 | -------------------------------------------------------------------------------- /view/adminhtml/templates/dashboard/chart.phtml: -------------------------------------------------------------------------------- 1 | 22 |
23 | isEnabledChart()): ?> 24 | getChartData(); ?> 25 | 27 | 28 |
29 | here", $block->getUrl('adminhtml/system_config/edit', ['section' => 'mageplaza_reports', 'store' => $block->getRequest()->getParam('store')])) ?> 30 |
31 | 32 |
33 | -------------------------------------------------------------------------------- /view/adminhtml/templates/dashboard/conversion_funnel.phtml: -------------------------------------------------------------------------------- 1 | 24 |
25 | image 27 |
28 | getProductViews()) ?> 29 |
30 |
31 | getAllCartItems()) ?> 32 |
33 |
34 | getAllOrderItem()) ?> 35 |
36 |
37 | -------------------------------------------------------------------------------- /view/adminhtml/templates/dashboard/sales_by_location.phtml: -------------------------------------------------------------------------------- 1 | 22 |
23 | getCollection())): ?> 24 | 25 | 26 | getCollection() as $item): ?> 27 | 28 | 29 | 30 | isCompare()): ?> 31 | 32 | 33 | 34 | 35 | 36 |
37 | 38 |
escapeHtml(__('We couldn\'t find any records.')) ?>
39 | 40 |
41 | -------------------------------------------------------------------------------- /view/adminhtml/templates/menu.phtml: -------------------------------------------------------------------------------- 1 | getGridName(); 26 | try { 27 | $date = $block->getDateRange(); 28 | } catch (Exception $e) { 29 | $date = []; 30 | } 31 | $menu = $block->getMenuUrls() ?: []; 32 | $storeFilterUrl = $block->getUrl('mpreports/details/storefilter', ['form_key' => $block->getFormKey()]); 33 | ?> 34 | 113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /view/adminhtml/templates/popup.phtml: -------------------------------------------------------------------------------- 1 | 22 | 25 | 61 | -------------------------------------------------------------------------------- /view/adminhtml/templates/switch.phtml: -------------------------------------------------------------------------------- 1 | 22 |
23 |
24 |
25 |
26 | escapeHtml(__('Mageplaza Advanced Reporting')) ?> 27 |
28 |
29 | escapeHtml( 30 | __('The better reports provides ecommerce insights across performance metrics, product sales, customers, and cost of goods sold. Instantly measure metrics like customer lifetime value, gross margin, net profit, and more to build and automate high-quality ecommerce reports.') 31 | ) ?> 32 |
33 |
34 | 41 |
42 | 52 | -------------------------------------------------------------------------------- /view/adminhtml/templates/system/config/link_builder.phtml: -------------------------------------------------------------------------------- 1 | 22 | 23 | 26 | 35 | 36 | 78 | 98 | 99 | -------------------------------------------------------------------------------- /view/adminhtml/web/css/detail/menu.css: -------------------------------------------------------------------------------- 1 | 22 | 23 | #menu { 24 | height: 50px; 25 | background-color: #cccccc; 26 | margin-bottom: 20px; 27 | } 28 | 29 | .fa.fa-bars { 30 | font-size: 20px; 31 | margin-top: -4px; 32 | margin-left: -10px; 33 | } 34 | 35 | .menu-select { 36 | float: left; 37 | } 38 | 39 | .menu-select .admin__action-dropdown { 40 | height: 28px; 41 | margin: 11px; 42 | } 43 | 44 | .menu-select .admin__action-dropdown-menu { 45 | left: 0; 46 | right: auto; 47 | width: 200px; 48 | z-index: 99; 49 | } 50 | 51 | .menu-select .admin__action-dropdown-menu a:hover { 52 | background-color: #cccccc; 53 | } 54 | 55 | .mp-rp-store-switcher, .date-range-picker, .customer-group { 56 | float: left; 57 | } 58 | 59 | .period, .order_status, .date_used { 60 | float: right; 61 | margin-right: 20px; 62 | } 63 | 64 | .mp-rp-store-switcher, .customer-group { 65 | margin-left: 20px; 66 | margin-right: 20px; 67 | } 68 | 69 | #daterange { 70 | margin-left: 20px; 71 | border: 1px solid #cccccc; 72 | height: 29px; 73 | } 74 | 75 | #daterange, #customer-group-id, #period { 76 | margin-top: 11px; 77 | padding: 4px; 78 | background-color: #ffffff; 79 | border: none; 80 | } 81 | 82 | .store-switcher.store-view span.store-switcher-label { 83 | display: none; 84 | } 85 | 86 | .admin__field-tooltip.tooltip { 87 | display: none; 88 | } 89 | 90 | .sticky-header { 91 | top: 0; 92 | } 93 | 94 | button#store-change-button { 95 | border: 1px solid #cccccc; 96 | background-color: #ffffff; 97 | padding-bottom: 4px; 98 | margin-top: 3px; 99 | } 100 | 101 | .store-switcher .dropdown .dropdown-menu[data-role="stores-list"], .admin__action-dropdown-wrap.active .admin__action-dropdown-menu { 102 | z-index: 999; 103 | } 104 | 105 | .mp_menu .select-box { 106 | margin-top: 11px; 107 | padding: 4px; 108 | background-color: #ffffff; 109 | border: none; 110 | } 111 | 112 | .order_status .label { 113 | float: left; 114 | margin-right: 5px; 115 | margin-top: 15px 116 | } 117 | 118 | .mp-rp-order-status.admin__action-dropdown-wrap.admin__data-grid-action-columns { 119 | float: right; 120 | margin-top: 11px; 121 | } 122 | 123 | button#mp-rp-order-status { 124 | padding: 4px 30px 5px 10px; 125 | } 126 | 127 | button#mp-rp-order-status:before{ 128 | content: none; 129 | } -------------------------------------------------------------------------------- /view/adminhtml/web/css/source/_module.less: -------------------------------------------------------------------------------- 1 | /** 2 | * Mageplaza 3 | * 4 | * NOTICE OF LICENSE 5 | * 6 | * This source file is subject to the Mageplaza.com license that is 7 | * available through the world-wide-web at this URL: 8 | * https://www.mageplaza.com/LICENSE.txt 9 | * 10 | * DISCLAIMER 11 | * 12 | * Do not edit or add to this file if you wish to upgrade this extension to newer 13 | * version in the future. 14 | * 15 | * @category Mageplaza 16 | * @package Mageplaza_Reports 17 | * @copyright Copyright (c) Mageplaza (https://www.mageplaza.com/) 18 | * @license https://www.mageplaza.com/LICENSE.txt 19 | */ 20 | 21 | li#menu-mageplaza-reports-dashboard ~ li#menu-magento-backend-dashboard { 22 | display: none; 23 | } 24 | 25 | #mp-reports-builder { 26 | margin-bottom: 10px; 27 | } 28 | 29 | #mp-reports-link-builder-input { 30 | background: #fff !important; 31 | font-size: 14px; 32 | cursor: auto !important; 33 | display: table-cell; 34 | color: #2e2e2e !important; 35 | border: 1px solid #e5e5e5 !important; 36 | opacity: 1 !important; 37 | border-radius: 3px 0 0 3px !important; 38 | width: calc(~"100% - 35px") !important; 39 | float: left; 40 | } 41 | 42 | #mp-reports-link-builder-copy { 43 | margin-left: 0; 44 | background-color: #fafafa; 45 | color: #2e2e2e; 46 | padding: 6px 10px; 47 | border: 1px solid #e5e5e5; 48 | border-radius: 0 3px 3px 0; 49 | font-size: 14px; 50 | font-weight: 400; 51 | border-left: none; 52 | 53 | &:active { 54 | background-color: #e1e1e1; 55 | border-color: #e3e3e3; 56 | color: #2e2e2e; 57 | } 58 | } 59 | 60 | .mp-tooltipped { 61 | position: relative; 62 | 63 | &:before { 64 | position: absolute; 65 | z-index: 1000001; 66 | display: none; 67 | width: 0; 68 | height: 0; 69 | color: rgba(0, 0, 0, .8); 70 | pointer-events: none; 71 | content: ""; 72 | border: 5px solid transparent; 73 | top: auto; 74 | right: 50%; 75 | bottom: -5px; 76 | margin-right: -5px; 77 | border-bottom-color: rgba(0, 0, 0, .8) 78 | } 79 | 80 | &:after { 81 | position: absolute; 82 | z-index: 1000000; 83 | display: none; 84 | padding: 5px 8px; 85 | font: normal normal 11px/1.5 Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol"; 86 | color: #fff; 87 | text-align: center; 88 | text-decoration: none; 89 | text-shadow: none; 90 | text-transform: none; 91 | letter-spacing: normal; 92 | word-wrap: break-word; 93 | white-space: pre; 94 | pointer-events: none; 95 | content: attr(aria-label); 96 | background: rgba(0, 0, 0, .8); 97 | border-radius: 3px; 98 | -webkit-font-smoothing: subpixel-antialiased; 99 | top: 100%; 100 | right: 50%; 101 | margin-top: 5px; 102 | -webkit-transform: translateX(50%); 103 | -ms-transform: translateX(50%); 104 | transform: translateX(50%) 105 | } 106 | 107 | &:hover, &:active, &:focus { 108 | &:before, &:after { 109 | display: inline-block; 110 | text-decoration: none 111 | } 112 | } 113 | } -------------------------------------------------------------------------------- /view/base/requirejs-config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Mageplaza 3 | * 4 | * NOTICE OF LICENSE 5 | * 6 | * This source file is subject to the Mageplaza.com license that is 7 | * available through the world-wide-web at this URL: 8 | * https://www.mageplaza.com/LICENSE.txt 9 | * 10 | * DISCLAIMER 11 | * 12 | * Do not edit or add to this file if you wish to upgrade this extension to newer 13 | * version in the future. 14 | * 15 | * @category Mageplaza 16 | * @package Mageplaza_Reports 17 | * @copyright Copyright (c) Mageplaza (https://www.mageplaza.com/) 18 | * @license https://www.mageplaza.com/LICENSE.txt 19 | */ 20 | 21 | var config = { 22 | paths: { 23 | gridstack: 'Mageplaza_Reports/js/lib/gridstack', 24 | gridstackJqueryUi: 'Mageplaza_Reports/js/lib/gridstack.jQueryUI', 25 | daterangepicker: 'Mageplaza_Reports/js/lib/daterangepicker.min', 26 | chartBundle: 'Mageplaza_Reports/js/lib/Chart.bundle.min', 27 | initDateRange: 'Mageplaza_Reports/js/dashboard/initDateRange', 28 | initGridStack: 'Mageplaza_Reports/js/dashboard/initGridStack', 29 | initChart: 'Mageplaza_Reports/js/dashboard/initChart', 30 | }, 31 | map: { 32 | gridstack: { 33 | 'lodash': 'underscore' 34 | }, 35 | gridstackJqueryUi: { 36 | 'lodash': 'underscore' 37 | }, 38 | '*': { 39 | moment: 'Mageplaza_Reports/js/lib/moment.min' 40 | } 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /view/base/web/css/images/conversion_funnel/funnel-container.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mageplaza/magento-2-reports/ea6c8ebba644a1492a3dc623d6117a756406cefd/view/base/web/css/images/conversion_funnel/funnel-container.png -------------------------------------------------------------------------------- /view/base/web/js/dashboard/initChart.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Mageplaza 3 | * 4 | * NOTICE OF LICENSE 5 | * 6 | * This source file is subject to the Mageplaza.com license that is 7 | * available through the world-wide-web at this URL: 8 | * https://www.mageplaza.com/LICENSE.txt 9 | * 10 | * DISCLAIMER 11 | * 12 | * Do not edit or add to this file if you wish to upgrade this extension to newer 13 | * version in the future. 14 | * 15 | * @category Mageplaza 16 | * @package Mageplaza_Reports 17 | * @copyright Copyright (c) Mageplaza (https://www.mageplaza.com/) 18 | * @license https://www.mageplaza.com/LICENSE.txt 19 | */ 20 | 21 | define([ 22 | 'jquery', 23 | 'Magento_Catalog/js/price-utils', 24 | 'chartBundle' 25 | ], function ($, priceUtils) { 26 | 'use strict'; 27 | $.widget('mageplaza.initChart', { 28 | options: { 29 | chartData: { 30 | yUnit: '' 31 | } 32 | }, 33 | _create: function () { 34 | var ctx = $('#' + this.options.chartData.name + '-chart'), 35 | data = { 36 | type: 'line', 37 | data: this.getData(), 38 | options: this.getOptions() 39 | }; 40 | 41 | new window.Chart(ctx, data); 42 | }, 43 | getOptions: function () { 44 | var self = this; 45 | 46 | return { 47 | fillColor: 'rgba(220,220,220,0.9)', 48 | legend: { 49 | display: true, 50 | 51 | position: 'bottom', 52 | labels: { 53 | usePointStyle: true, 54 | fontColor: '#333' 55 | } 56 | }, 57 | tooltips: { 58 | mode: 'index', 59 | intersect: true, 60 | callbacks: { 61 | label: function (tooltipItem, data) { 62 | var dataset = data.datasets[tooltipItem.datasetIndex], 63 | index = tooltipItem.index; 64 | 65 | return dataset.labels[index] + ': ' + 66 | (data.yUnit 67 | ? priceUtils.formatPrice(dataset.data[index], data.yUnit) 68 | : dataset.data[index]); 69 | }, 70 | title: function (tooltipItems, data) { 71 | if (data.index === 'repeatCustomerRate') { 72 | return tooltipItems[0].xLabel; 73 | } 74 | return ''; 75 | } 76 | } 77 | }, 78 | scales: { 79 | xAxes: [{ 80 | display: true, 81 | labelString: 'Time', 82 | type: 'time', 83 | // distribution: 'series', 84 | time: { 85 | stepSize: this.options.chartData.stepSize, 86 | unit: 'day', 87 | displayFormats: { 88 | day: 'Y-M-D' 89 | } 90 | }, 91 | ticks: { 92 | callback: function (label) { 93 | return label; 94 | } 95 | } 96 | }], 97 | yAxes: [{ 98 | scaleStartValue: 0, 99 | scaleLabel: { 100 | display: true, 101 | labelUnit: this.options.chartData.yUnit, 102 | labelString: this.options.chartData.yLabel, 103 | scaleStepWidth: 1 104 | }, 105 | ticks: { 106 | min: 0, 107 | // Include a currency sign in the ticks 108 | callback: function (value) { 109 | if (Math.floor(value) === value) { 110 | return self.options.chartData.yUnit 111 | ? priceUtils.formatPrice(value, self.options.chartData.yUnit) 112 | : value; 113 | } 114 | } 115 | } 116 | }] 117 | } 118 | }; 119 | }, 120 | getData: function () { 121 | var data = { 122 | index: this.options.chartData.name, 123 | labels: this.options.chartData['labels'], 124 | yUnit: this.options.chartData['yUnit'], 125 | datasets: [ 126 | { 127 | labels: this.options.chartData['data']['labels'], 128 | label: this.options.chartData['label'][0], 129 | data: this.options.chartData['data']['data'], 130 | backgroundColor: '#977bca', 131 | borderColor: '#6f42c1', 132 | borderWidth: 1, 133 | lineTension: 0.5, //cong 134 | pointBorderWidth: 1, 135 | pointBorderColor: 'transparent', 136 | fill: this.options.chartData.isFill 137 | } 138 | ] 139 | }; 140 | var compareDataset = this.getCompareDataSet(); 141 | 142 | if (this.options.chartData.isCompare === '1' || this.options.chartData.name === 'repeatCustomerRate') { 143 | data.datasets.push(compareDataset); 144 | } 145 | 146 | return data; 147 | }, 148 | getCompareDataSet: function () { 149 | return { 150 | labels: this.options.chartData['compareData']['labels'], 151 | label: this.options.chartData['label'][1], 152 | data: this.options.chartData['compareData']['data'], 153 | backgroundColor: '#59a7b3', 154 | borderColor: '#17a2b8', 155 | borderWidth: 1, 156 | lineTension: 0.5, 157 | pointBorderColor: 'transparent', 158 | // pointBackgroundColor: 'transparent', 159 | // pointHoverBackgroundColor: 'blue', 160 | fill: this.options.chartData.isFill 161 | }; 162 | } 163 | }); 164 | 165 | return $.mageplaza.initChart; 166 | }); 167 | -------------------------------------------------------------------------------- /view/base/web/js/dashboard/initDateRange.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Mageplaza 3 | * 4 | * NOTICE OF LICENSE 5 | * 6 | * This source file is subject to the Mageplaza.com license that is 7 | * available through the world-wide-web at this URL: 8 | * https://www.mageplaza.com/LICENSE.txt 9 | * 10 | * DISCLAIMER 11 | * 12 | * Do not edit or add to this file if you wish to upgrade this extension to newer 13 | * version in the future. 14 | * 15 | * @category Mageplaza 16 | * @package Mageplaza_Reports 17 | * @copyright Copyright (c) Mageplaza (https://www.mageplaza.com/) 18 | * @license https://www.mageplaza.com/LICENSE.txt 19 | */ 20 | 21 | define([ 22 | 'jquery', 23 | 'moment', 24 | 'daterangepicker' 25 | ], function ($, moment) { 26 | 'use strict'; 27 | 28 | var dateRangeEl = $('#daterange'), 29 | compareDateRangeEl = $('#compare-daterange'), 30 | dashboardContainer = $('.dashboard-container'); 31 | 32 | $.widget('mageplaza.initDateRange', { 33 | options: { 34 | url: '', 35 | isCompare: '' 36 | }, 37 | 38 | _create: function () { 39 | this.initNowDateRange(); 40 | if (this.options.isCompare) { 41 | this.initCompareDateRange(); 42 | this.initNowDateRangeHideObserver(); 43 | } 44 | this.initNowDateRangeObserver(); 45 | }, 46 | 47 | initDateRange: function (el, start, end, data) { 48 | function cb(cbStart, cbEnd) { 49 | el.find('span').html(cbStart.format('MMM DD, YYYY') + ' - ' + cbEnd.format('MMM DD, YYYY')); 50 | } 51 | 52 | el.daterangepicker(data, cb); 53 | cb(start, end); 54 | }, 55 | 56 | initNowDateRange: function () { 57 | var start = moment(this.options.date[0]), 58 | end = moment(this.options.date[1]), 59 | dateRangeData = { 60 | startDate: start, 61 | endDate: end, 62 | ranges: { 63 | 'Today': [moment(), moment()], 64 | 'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')], 65 | 'Last 7 Days': [moment().subtract(6, 'days'), moment()], 66 | 'Last 30 Days': [moment().subtract(29, 'days'), moment()], 67 | 'This Month': [moment().startOf('month'), moment().endOf('month')], 68 | 'Last Month': [ 69 | moment().subtract(1, 'month').startOf('month'), 70 | moment().subtract(1, 'month').endOf('month') 71 | ], 72 | 'YTD': [moment().subtract(1, 'year'), moment()], 73 | '2YTD': [moment().subtract(2, 'year'), moment()] 74 | } 75 | }; 76 | 77 | this.initDateRange(dateRangeEl, start, end, dateRangeData); 78 | }, 79 | 80 | initCompareDateRange: function (initStartDate, initEndDate) { 81 | var self = this, 82 | startDate = initStartDate || dateRangeEl.data().startDate, 83 | endDate = initEndDate || dateRangeEl.data().endDate, 84 | days = endDate.diff(startDate, 'days'), 85 | compareEndDate = moment(startDate.format('Y-MM-DD')).subtract(1, 'days'), 86 | compareStartDate = moment(compareEndDate.format('Y-MM-DD')).subtract(days, 'days').endOf('days'), 87 | compareDateRangeData = { 88 | startDate: compareStartDate, 89 | endDate: compareEndDate, 90 | ranges: { 91 | 'Previous period': [compareStartDate, compareEndDate], 92 | 'Previous year': [ 93 | moment(startDate.format('Y-MM-DD')).subtract(1, 'year'), 94 | moment(endDate.format('Y-MM-DD')).subtract(1, 'year') 95 | ] 96 | } 97 | }; 98 | 99 | this.initDateRange(compareDateRangeEl, compareStartDate, compareEndDate, compareDateRangeData); 100 | 101 | compareDateRangeEl.on('apply.daterangepicker', function (ev, picker) { 102 | compareDateRangeEl.data().startDate = picker.startDate; 103 | compareDateRangeEl.data().endDate = picker.endDate; 104 | var data = { 105 | dateRange: { 106 | 0: dateRangeEl.data().startDate.format('Y-MM-DD H:m:s'), 107 | 1: dateRangeEl.data().endDate.format('Y-MM-DD H:m:s'), 108 | 2: picker.startDate.format('Y-MM-DD H:m:s'), 109 | 3: picker.endDate.format('Y-MM-DD H:m:s') 110 | } 111 | }; 112 | 113 | self.ajaxSubmit(data); 114 | }); 115 | }, 116 | 117 | ajaxSubmit: function (data) { 118 | $.ajax({ 119 | url: this.options.url, 120 | data: data, 121 | type: 'POST', 122 | showLoader: true, 123 | success: function (res) { 124 | var dashboard = $('
' + res.dashboard + '
').find('.dashboard-container'); 125 | 126 | dashboardContainer.html(dashboard.html()); 127 | dashboardContainer.trigger('contentUpdated'); 128 | }, 129 | }); 130 | }, 131 | 132 | initNowDateRangeObserver: function () { 133 | var self = this; 134 | 135 | dateRangeEl.on('apply.daterangepicker', function (ev, picker) { 136 | var data; 137 | 138 | dateRangeEl.data().startDate = picker.startDate; 139 | dateRangeEl.data().endDate = picker.endDate; 140 | data = { 141 | dateRange: { 142 | 0: picker.startDate.format('Y-MM-DD H:m:s'), 143 | 1: picker.endDate.format('Y-MM-DD H:m:s'), 144 | 2: self.options.isCompare ? compareDateRangeEl.data().startDate.format('Y-MM-DD H:m:s') : null, 145 | 3: self.options.isCompare ? compareDateRangeEl.data().endDate.format('Y-MM-DD H:m:s') : null 146 | } 147 | }; 148 | 149 | self.ajaxSubmit(data); 150 | }); 151 | }, 152 | 153 | initNowDateRangeHideObserver: function () { 154 | var self = this; 155 | 156 | dateRangeEl.on('hide.daterangepicker', function (ev, picker) { 157 | self.initCompareDateRange(picker.startDate, picker.endDate); 158 | }); 159 | } 160 | }); 161 | 162 | return $.mageplaza.initDateRange; 163 | }); 164 | -------------------------------------------------------------------------------- /view/base/web/js/dashboard/initGridStack.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Mageplaza 3 | * 4 | * NOTICE OF LICENSE 5 | * 6 | * This source file is subject to the Mageplaza.com license that is 7 | * available through the world-wide-web at this URL: 8 | * https://www.mageplaza.com/LICENSE.txt 9 | * 10 | * DISCLAIMER 11 | * 12 | * Do not edit or add to this file if you wish to upgrade this extension to newer 13 | * version in the future. 14 | * 15 | * @category Mageplaza 16 | * @package Mageplaza_Reports 17 | * @copyright Copyright (c) Mageplaza (https://www.mageplaza.com/) 18 | * @license https://www.mageplaza.com/LICENSE.txt 19 | */ 20 | 21 | define([ 22 | 'jquery', 23 | 'underscore', 24 | 'Magento_Ui/js/modal/alert', 25 | 'gridstack', 26 | 'gridstackJqueryUi', 27 | 'touchPunch' 28 | ], function ($, _, uiAlert) { 29 | 'use strict'; 30 | 31 | $.widget('mageplaza.initGridStack', { 32 | options: { 33 | url: '', 34 | loadCardUrl: '', 35 | gridWidget: [] 36 | }, 37 | _create: function () { 38 | this.initGrid(); 39 | this.changeCardPositionObs(); 40 | this.toggleCardTable(); 41 | this.toggleCardVisible(); 42 | this.element.trigger('mpCardLoaded'); 43 | }, 44 | toggleCardTable: function () { 45 | var cardsTableEl = $('.mp-ar-card.admin__action-dropdown-wrap.admin__data-grid-action-columns'); 46 | 47 | $('button#mp-ar-card').on('click', function () { 48 | if (cardsTableEl.hasClass('_active')) { 49 | cardsTableEl.removeClass('_active'); 50 | } else { 51 | cardsTableEl.addClass('_active'); 52 | } 53 | }); 54 | $('body').on('click', function (e) { 55 | if (!$(e.target).parents().hasClass('mp-ar-card')) { 56 | cardsTableEl.removeClass('_active'); 57 | } 58 | }); 59 | }, 60 | changeCardPositionObs: function () { 61 | var self = this; 62 | var gridStackEl = $('.grid-stack'); 63 | 64 | gridStackEl.on('change', function (event, items) { 65 | var data = {}; 66 | 67 | if (items === undefined) { 68 | return; 69 | } 70 | _.each(items, function (item) { 71 | data[item.id] = { 72 | 'x': item.x, 73 | 'y': item.y, 74 | 'width': item.width, 75 | 'height': item.height 76 | }; 77 | }); 78 | 79 | self.saveCardPosition(data); 80 | }); 81 | }, 82 | toggleCardVisible: function () { 83 | var self = this; 84 | 85 | $('.admin__action-dropdown-menu-content .admin__control-checkbox').each(function () { 86 | $(this).change(function () { 87 | var cardId = $(this).attr('data-card-id'), 88 | cardEl = $('#' + cardId), 89 | card, dateRange, 90 | dateRangeEl = $('.ar_dashboard #daterange'), 91 | compareDateRangEl = $('.ar_dashboard #compare-daterange'); 92 | 93 | if (cardEl.length < 1) { 94 | card = this; 95 | dateRange = [ 96 | dateRangeEl.data('startDate').format('Y-MM-DD'), 97 | dateRangeEl.data('endDate').format('Y-MM-DD'), 98 | ]; 99 | if (compareDateRangEl.length) { 100 | dateRange[2] = compareDateRangEl.data('startDate').format('Y-MM-DD'); 101 | dateRange[3] = compareDateRangEl.data('endDate').format('Y-MM-DD'); 102 | } else { 103 | dateRange[2] = dateRange[3] = null; 104 | } 105 | $.ajax({ 106 | url: self.options.loadCardUrl, 107 | data: {card_id: cardId, dateRange: dateRange}, 108 | showLoader: true, 109 | success: function (result) { 110 | if (cardEl.length < 1) { 111 | if (result.html) { 112 | $('.grid-stack').append(result.html); 113 | } else { 114 | uiAlert({ 115 | content: result.message 116 | }); 117 | return; 118 | } 119 | } 120 | self.changeCard(card, cardId); 121 | $('#' + cardId).trigger('contentUpdated'); 122 | } 123 | }); 124 | } else { 125 | self.changeCard(this, cardId); 126 | } 127 | }); 128 | }); 129 | }, 130 | changeCard: function (card, cardId) { 131 | var cardEl = $('#' + cardId), 132 | data = {}; 133 | 134 | if (card.checked) { 135 | cardEl.removeClass('hide'); 136 | this.options.grid.addWidget(cardEl); 137 | } else { 138 | this.options.grid.removeWidget(cardEl, 0); 139 | cardEl.attr('data-gs-y', 100).attr('data-gs-x', 0); 140 | cardEl.addClass('hide'); 141 | } 142 | data[cardId] = { 143 | 'visible': card.checked ? 1 : 0, 144 | 'x': cardEl.attr('data-gs-x'), 145 | 'y': cardEl.attr('data-gs-y'), 146 | 'width': cardEl.attr('data-gs-width'), 147 | 'height': cardEl.attr('data-gs-height') 148 | }; 149 | // save card position when show/hide card 150 | this.saveCardPosition(data); 151 | }, 152 | saveCardPosition: function (data) { 153 | $.ajax({ 154 | url: this.options.url, 155 | data: {items: data}, 156 | type: 'POST' 157 | }); 158 | }, 159 | initGrid: function () { 160 | var gridStackEl = $('.grid-stack'); 161 | var options = { 162 | alwaysShowResizeHandle: 163 | /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent), 164 | cellHeight: 30, 165 | verticalMargin: 10, 166 | draggable: {handle: '.draggable', scroll: true, appendTo: 'body'}, 167 | }; 168 | 169 | gridStackEl.gridstack(options); 170 | this.options.grid = gridStackEl.data('gridstack'); 171 | } 172 | }); 173 | 174 | return $.mageplaza.initGridStack; 175 | }); 176 | -------------------------------------------------------------------------------- /view/base/web/js/lib/gridstack.jQueryUI.js: -------------------------------------------------------------------------------- 1 | /** 2 | * gridstack.js 1.0.0-dev 3 | * http://troolee.github.io/gridstack.js/ 4 | * (c) 2014-2017 Pavel Reznikov, Dylan Weiss 5 | * gridstack.js may be freely distributed under the MIT license. 6 | * @preserve 7 | */ 8 | (function(factory) { 9 | if (typeof define === 'function' && define.amd) { 10 | define(['jquery', 'lodash', 'gridstack', 'jquery/ui'], factory); 11 | } else if (typeof exports !== 'undefined') { 12 | try { jQuery = require('jquery'); } catch (e) {} 13 | try { _ = require('lodash'); } catch (e) {} 14 | try { GridStackUI = require('gridstack'); } catch (e) {} 15 | factory(jQuery, _, GridStackUI); 16 | } else { 17 | factory(jQuery, _, GridStackUI); 18 | } 19 | })(function($, _, GridStackUI) { 20 | 21 | var scope = window; 22 | 23 | /** 24 | * @class JQueryUIGridStackDragDropPlugin 25 | * jQuery UI implementation of drag'n'drop gridstack plugin. 26 | */ 27 | function JQueryUIGridStackDragDropPlugin(grid) { 28 | GridStackUI.GridStackDragDropPlugin.call(this, grid); 29 | } 30 | 31 | GridStackUI.GridStackDragDropPlugin.registerPlugin(JQueryUIGridStackDragDropPlugin); 32 | 33 | JQueryUIGridStackDragDropPlugin.prototype = Object.create(GridStackUI.GridStackDragDropPlugin.prototype); 34 | JQueryUIGridStackDragDropPlugin.prototype.constructor = JQueryUIGridStackDragDropPlugin; 35 | 36 | JQueryUIGridStackDragDropPlugin.prototype.resizable = function(el, opts) { 37 | el = $(el); 38 | if (opts === 'disable' || opts === 'enable') { 39 | el.resizable(opts); 40 | } else if (opts === 'option') { 41 | var key = arguments[2]; 42 | var value = arguments[3]; 43 | el.resizable(opts, key, value); 44 | } else { 45 | var handles = el.data('gs-resize-handles') ? el.data('gs-resize-handles') : 46 | this.grid.opts.resizable.handles; 47 | el.resizable(_.extend({}, this.grid.opts.resizable, { 48 | handles: handles 49 | }, { 50 | start: opts.start || function() {}, 51 | stop: opts.stop || function() {}, 52 | resize: opts.resize || function() {} 53 | })); 54 | } 55 | return this; 56 | }; 57 | 58 | JQueryUIGridStackDragDropPlugin.prototype.draggable = function(el, opts) { 59 | el = $(el); 60 | if (opts === 'disable' || opts === 'enable') { 61 | el.draggable(opts); 62 | } else { 63 | el.draggable(_.extend({}, this.grid.opts.draggable, { 64 | containment: this.grid.opts.isNested ? this.grid.container.parent() : null, 65 | start: opts.start || function() {}, 66 | stop: opts.stop || function() {}, 67 | drag: opts.drag || function() {} 68 | })); 69 | } 70 | return this; 71 | }; 72 | 73 | JQueryUIGridStackDragDropPlugin.prototype.droppable = function(el, opts) { 74 | el = $(el); 75 | el.droppable(opts); 76 | return this; 77 | }; 78 | 79 | JQueryUIGridStackDragDropPlugin.prototype.isDroppable = function(el, opts) { 80 | el = $(el); 81 | return Boolean(el.data('droppable')); 82 | }; 83 | 84 | JQueryUIGridStackDragDropPlugin.prototype.on = function(el, eventName, callback) { 85 | $(el).on(eventName, callback); 86 | return this; 87 | }; 88 | 89 | return JQueryUIGridStackDragDropPlugin; 90 | }); -------------------------------------------------------------------------------- /view/frontend/layout/mpreports_dashboard_index.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 0 37 | adminhtml 38 | 0 39 | 1 40 | 0 41 | All Websites 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /view/frontend/web/css/fonts/admin-icons/admin-icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mageplaza/magento-2-reports/ea6c8ebba644a1492a3dc623d6117a756406cefd/view/frontend/web/css/fonts/admin-icons/admin-icons.eot -------------------------------------------------------------------------------- /view/frontend/web/css/fonts/admin-icons/admin-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mageplaza/magento-2-reports/ea6c8ebba644a1492a3dc623d6117a756406cefd/view/frontend/web/css/fonts/admin-icons/admin-icons.ttf -------------------------------------------------------------------------------- /view/frontend/web/css/fonts/admin-icons/admin-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mageplaza/magento-2-reports/ea6c8ebba644a1492a3dc623d6117a756406cefd/view/frontend/web/css/fonts/admin-icons/admin-icons.woff -------------------------------------------------------------------------------- /view/frontend/web/css/fonts/admin-icons/admin-icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mageplaza/magento-2-reports/ea6c8ebba644a1492a3dc623d6117a756406cefd/view/frontend/web/css/fonts/admin-icons/admin-icons.woff2 -------------------------------------------------------------------------------- /view/frontend/web/css/fonts/opensans/bold/opensans-700.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mageplaza/magento-2-reports/ea6c8ebba644a1492a3dc623d6117a756406cefd/view/frontend/web/css/fonts/opensans/bold/opensans-700.eot -------------------------------------------------------------------------------- /view/frontend/web/css/fonts/opensans/bold/opensans-700.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mageplaza/magento-2-reports/ea6c8ebba644a1492a3dc623d6117a756406cefd/view/frontend/web/css/fonts/opensans/bold/opensans-700.ttf -------------------------------------------------------------------------------- /view/frontend/web/css/fonts/opensans/bold/opensans-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mageplaza/magento-2-reports/ea6c8ebba644a1492a3dc623d6117a756406cefd/view/frontend/web/css/fonts/opensans/bold/opensans-700.woff -------------------------------------------------------------------------------- /view/frontend/web/css/fonts/opensans/bold/opensans-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mageplaza/magento-2-reports/ea6c8ebba644a1492a3dc623d6117a756406cefd/view/frontend/web/css/fonts/opensans/bold/opensans-700.woff2 -------------------------------------------------------------------------------- /view/frontend/web/css/fonts/opensans/light/opensans-300.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mageplaza/magento-2-reports/ea6c8ebba644a1492a3dc623d6117a756406cefd/view/frontend/web/css/fonts/opensans/light/opensans-300.eot -------------------------------------------------------------------------------- /view/frontend/web/css/fonts/opensans/light/opensans-300.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mageplaza/magento-2-reports/ea6c8ebba644a1492a3dc623d6117a756406cefd/view/frontend/web/css/fonts/opensans/light/opensans-300.ttf -------------------------------------------------------------------------------- /view/frontend/web/css/fonts/opensans/light/opensans-300.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mageplaza/magento-2-reports/ea6c8ebba644a1492a3dc623d6117a756406cefd/view/frontend/web/css/fonts/opensans/light/opensans-300.woff -------------------------------------------------------------------------------- /view/frontend/web/css/fonts/opensans/light/opensans-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mageplaza/magento-2-reports/ea6c8ebba644a1492a3dc623d6117a756406cefd/view/frontend/web/css/fonts/opensans/light/opensans-300.woff2 -------------------------------------------------------------------------------- /view/frontend/web/css/fonts/opensans/regular/opensans-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mageplaza/magento-2-reports/ea6c8ebba644a1492a3dc623d6117a756406cefd/view/frontend/web/css/fonts/opensans/regular/opensans-400.eot -------------------------------------------------------------------------------- /view/frontend/web/css/fonts/opensans/regular/opensans-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mageplaza/magento-2-reports/ea6c8ebba644a1492a3dc623d6117a756406cefd/view/frontend/web/css/fonts/opensans/regular/opensans-400.ttf -------------------------------------------------------------------------------- /view/frontend/web/css/fonts/opensans/regular/opensans-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mageplaza/magento-2-reports/ea6c8ebba644a1492a3dc623d6117a756406cefd/view/frontend/web/css/fonts/opensans/regular/opensans-400.woff -------------------------------------------------------------------------------- /view/frontend/web/css/fonts/opensans/regular/opensans-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mageplaza/magento-2-reports/ea6c8ebba644a1492a3dc623d6117a756406cefd/view/frontend/web/css/fonts/opensans/regular/opensans-400.woff2 -------------------------------------------------------------------------------- /view/frontend/web/css/fonts/opensans/semibold/opensans-600.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mageplaza/magento-2-reports/ea6c8ebba644a1492a3dc623d6117a756406cefd/view/frontend/web/css/fonts/opensans/semibold/opensans-600.eot -------------------------------------------------------------------------------- /view/frontend/web/css/fonts/opensans/semibold/opensans-600.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mageplaza/magento-2-reports/ea6c8ebba644a1492a3dc623d6117a756406cefd/view/frontend/web/css/fonts/opensans/semibold/opensans-600.ttf -------------------------------------------------------------------------------- /view/frontend/web/css/fonts/opensans/semibold/opensans-600.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mageplaza/magento-2-reports/ea6c8ebba644a1492a3dc623d6117a756406cefd/view/frontend/web/css/fonts/opensans/semibold/opensans-600.woff -------------------------------------------------------------------------------- /view/frontend/web/css/fonts/opensans/semibold/opensans-600.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mageplaza/magento-2-reports/ea6c8ebba644a1492a3dc623d6117a756406cefd/view/frontend/web/css/fonts/opensans/semibold/opensans-600.woff2 -------------------------------------------------------------------------------- /view/frontend/web/css/images/loader-1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mageplaza/magento-2-reports/ea6c8ebba644a1492a3dc623d6117a756406cefd/view/frontend/web/css/images/loader-1.gif -------------------------------------------------------------------------------- /view/frontend/web/js/store-switcher.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Mageplaza 3 | * 4 | * NOTICE OF LICENSE 5 | * 6 | * This source file is subject to the Mageplaza.com license that is 7 | * available through the world-wide-web at this URL: 8 | * https://www.mageplaza.com/LICENSE.txt 9 | * 10 | * DISCLAIMER 11 | * 12 | * Do not edit or add to this file if you wish to upgrade this extension to newer 13 | * version in the future. 14 | * 15 | * @category Mageplaza 16 | * @package Mageplaza_Reports 17 | * @copyright Copyright (c) Mageplaza (https://www.mageplaza.com/) 18 | * @license https://www.mageplaza.com/LICENSE.txt 19 | */ 20 | 21 | define([ 22 | 'jquery' 23 | ], function ($) { 24 | 'use strict'; 25 | 26 | /** 27 | * @param {Object} storeSwitchConfig 28 | */ 29 | return function (storeSwitchConfig) { 30 | var scopeSwitcherHandler; 31 | 32 | (function () { 33 | var storesList = $('[data-role=stores-list]'); 34 | 35 | storesList.on('click', '[data-value]', function (event) { 36 | var val = $(event.target).data('value'), 37 | role = $(event.target).data('role'), 38 | switcher = $('[data-role=' + role + ']'); 39 | 40 | event.preventDefault(); 41 | 42 | if (!switcher.val() || val !== switcher.val()) { 43 | 44 | /* Set the value & trigger event */ 45 | switcher.val(val).trigger('change'); 46 | } 47 | }); 48 | })($); 49 | 50 | /** 51 | * Switch store scope 52 | * 53 | * @param {Object} obj 54 | * @return void 55 | */ 56 | function switchScope (obj) { 57 | var switcher = $(obj), 58 | scopeId = switcher.val(), 59 | scopeParams = '', 60 | switcherParams = {}; 61 | 62 | if (scopeId) { 63 | scopeParams = switcher.data('param') + '/' + scopeId + '/'; 64 | } 65 | 66 | if (obj.switchParams) { 67 | scopeParams += obj.switchParams; 68 | } 69 | 70 | /** 71 | * Reload function for switcher 72 | */ 73 | function reload () { 74 | var url; 75 | 76 | if (!storeSwitchConfig.isUsingIframe) { 77 | 78 | if (storeSwitchConfig.switchUrl && storeSwitchConfig.switchUrl.length > 0) { 79 | url = storeSwitchConfig.switchUrl + scopeParams; 80 | 81 | /* eslint-disable no-undef */ 82 | setLocation(url); 83 | } 84 | 85 | } else { 86 | $('#preview_selected_store').val(scopeId); 87 | $('#preview_form').trigger('submit'); 88 | 89 | $('.store-switcher .dropdown-menu li a').each(function () { 90 | var $this = $(this); 91 | 92 | if ($this.data('role') === 'store-view-id' && $this.data('value') === scopeId) { 93 | $('#store-change-button').html($this.text()); 94 | } 95 | }); 96 | 97 | $('#store-change-button').click(); 98 | } 99 | } 100 | 101 | if (typeof scopeSwitcherHandler !== 'undefined') { 102 | switcherParams = { 103 | scopeId: scopeId, 104 | scopeParams: scopeParams, 105 | useConfirm: storeSwitchConfig.useConfirm 106 | }; 107 | 108 | scopeSwitcherHandler(switcherParams); 109 | } else if (storeSwitchConfig.useConfirm) { 110 | require([ 111 | 'Magento_Ui/js/modal/confirm', 112 | 'mage/translate' 113 | ], function (confirm, $t) { 114 | confirm({ 115 | content: $t('Please confirm scope switching. All data that hasn\'t been saved will be lost.'), 116 | actions: { 117 | 118 | /** 119 | * Confirm action 120 | */ 121 | confirm: function () { 122 | reload(); 123 | }, 124 | 125 | /** 126 | * Cancel action 127 | */ 128 | cancel: function () { 129 | obj.value = storeSwitchConfig.storeId ? storeSwitchConfig.storeId : ''; 130 | } 131 | } 132 | }); 133 | }); 134 | } else { 135 | reload(); 136 | } 137 | } 138 | 139 | window.scopeSwitcherHandler = scopeSwitcherHandler; 140 | window.switchScope = switchScope; 141 | }; 142 | }); 143 | --------------------------------------------------------------------------------