diff --git a/assets/css/analytics.css b/assets/css/analytics.css new file mode 100644 index 0000000..0214f75 --- /dev/null +++ b/assets/css/analytics.css @@ -0,0 +1,136 @@ +/** + * Mailchimp Analytics Page Styles + * + * @package Mailchimp + */ + +/* Filters bar */ +.mailchimp-sf-analytics-wrapper { + max-width: 1200px; + margin: 20px 0; +} + +.mailchimp-sf-analytics-filters { + display: flex; + flex-wrap: wrap; + align-items: flex-end; + gap: 16px; + padding: 20px; + background: #fff; + border: 1px solid #dcdcde; + border-radius: 4px; + margin-bottom: 20px; +} + +.mailchimp-sf-analytics-filter-group { + display: flex; + flex-direction: column; + gap: 4px; +} + +.mailchimp-sf-analytics-filter-group label { + font-size: 12px; + font-weight: 600; + color: #1d2327; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.mailchimp-sf-analytics-filter-group select, +.mailchimp-sf-analytics-filter-group input[type="date"] { + min-width: 180px; + height: 36px; + padding: 0 8px; + border: 1px solid #8c8f94; + border-radius: 4px; + font-size: 14px; + color: #1d2327; + background-color: #fff; +} + +.mailchimp-sf-analytics-filter-group select:focus, +.mailchimp-sf-analytics-filter-group input[type="date"]:focus { + border-color: #007cba; + box-shadow: 0 0 0 1px #007cba; + outline: none; +} + +/* Custom date range row */ +.mailchimp-sf-custom-dates { + flex-direction: row; + align-items: flex-end; + gap: 8px; +} + +.mailchimp-sf-custom-dates label { + align-self: center; +} + +/* Resolved date range display */ +.mailchimp-sf-analytics-date-display { + margin-left: auto; + align-self: center; + font-size: 14px; + color: #50575e; + font-style: italic; +} + +/* Content area */ +.mailchimp-sf-analytics-content { + padding: 24px; + background: #fff; + border: 1px solid #dcdcde; + border-radius: 4px; + min-height: 200px; + margin-bottom: 20px; +} + +.mailchimp-sf-analytics-placeholder { + display: flex; + align-items: center; + justify-content: center; + min-height: 200px; + color: #8c8f94; + font-size: 15px; +} + +/* Deep link */ +.mailchimp-sf-analytics-deep-link { + margin-top: 16px; +} + +.mailchimp-sf-analytics-deep-link a { + display: inline-flex; + align-items: center; + gap: 6px; +} + +.mailchimp-sf-analytics-deep-link .dashicons { + font-size: 16px; + width: 16px; + height: 16px; + line-height: 16px; +} + +/* Responsive */ +@media screen and (max-width: 782px) { + .mailchimp-sf-analytics-filters { + flex-direction: column; + align-items: stretch; + } + + .mailchimp-sf-analytics-date-display { + margin-left: 0; + } + + .mailchimp-sf-custom-dates { + flex-direction: column; + align-items: stretch; + } + + .mailchimp-sf-analytics-filter-group select, + .mailchimp-sf-analytics-filter-group input[type="date"] { + min-width: auto; + width: 100%; + } +} diff --git a/assets/js/analytics.js b/assets/js/analytics.js new file mode 100644 index 0000000..1c7f085 --- /dev/null +++ b/assets/js/analytics.js @@ -0,0 +1,121 @@ +/** + * Mailchimp Analytics Page JavaScript + * + * @package Mailchimp + */ + +(function () { + const dateRangeSelect = document.getElementById('mailchimp-sf-date-range'); + const customDates = document.querySelector('.mailchimp-sf-custom-dates'); + const dateFrom = document.getElementById('mailchimp-sf-date-from'); + const dateTo = document.getElementById('mailchimp-sf-date-to'); + const listFilter = document.getElementById('mailchimp-sf-list-filter'); + const resolvedDisplay = document.getElementById('mailchimp-sf-resolved-date-range'); + + /** + * Format a Date object to a human-readable string. + * + * @param {Date} date Date to format. + * @returns {string} Formatted date string. + */ + function formatDate(date) { + return date.toLocaleDateString(undefined, { + year: 'numeric', + month: 'short', + day: 'numeric', + }); + } + + /** + * Get the resolved date range based on current filter selection. + * + * @returns {{ from: Date, to: Date }|null} Date range or null. + */ + function getDateRange() { + const { value } = dateRangeSelect; + let to = new Date(); + let from; + + if (value === 'custom') { + if (!dateFrom.value || !dateTo.value) { + return null; + } + from = new Date(`${dateFrom.value}T00:00:00`); + to = new Date(`${dateTo.value}T23:59:59`); + return { from, to }; + } + + const days = parseInt(value, 10); + from = new Date(); + from.setDate(from.getDate() - days); + return { from, to }; + } + + /** + * Update the resolved date range display text. + */ + function updateResolvedDateRange() { + const range = getDateRange(); + if (range) { + resolvedDisplay.textContent = `${formatDate(range.from)} \u2013 ${formatDate(range.to)}`; + } else { + resolvedDisplay.textContent = ''; + } + } + + /** + * Toggle visibility of custom date inputs. + */ + function toggleCustomDates() { + const isCustom = dateRangeSelect.value === 'custom'; + customDates.style.display = isCustom ? 'flex' : 'none'; + } + + /** + * Refresh analytics sections when filters change. + */ + function refreshAnalytics() { + updateResolvedDateRange(); + + const range = getDateRange(); + const listId = listFilter ? listFilter.value : ''; + + if (!range) { + return; + } + + // Dispatch a custom event so other scripts can listen for filter changes. + const event = new CustomEvent('mailchimp-analytics-refresh', { + detail: { + from: range.from.toISOString(), + to: range.to.toISOString(), + listId, + }, + }); + document.dispatchEvent(event); + } + + // Bind events. + if (dateRangeSelect) { + dateRangeSelect.addEventListener('change', function () { + toggleCustomDates(); + refreshAnalytics(); + }); + } + + if (dateFrom) { + dateFrom.addEventListener('change', refreshAnalytics); + } + + if (dateTo) { + dateTo.addEventListener('change', refreshAnalytics); + } + + if (listFilter) { + listFilter.addEventListener('change', refreshAnalytics); + } + + // Initialize on load. + toggleCustomDates(); + updateResolvedDateRange(); +})(); diff --git a/includes/admin/templates/analytics.php b/includes/admin/templates/analytics.php new file mode 100644 index 0000000..694d99d --- /dev/null +++ b/includes/admin/templates/analytics.php @@ -0,0 +1,89 @@ + +
+ + +
+
+
+

+ +

+

+ +

+
+
+
+ +
+
+
+
+ + +
+ + + +
+ + +
+ +
+ +
+
+ +
+
+

+
+
+ + + + +
+
+
diff --git a/includes/class-mailchimp-admin.php b/includes/class-mailchimp-admin.php index 3805336..002a542 100644 --- a/includes/class-mailchimp-admin.php +++ b/includes/class-mailchimp-admin.php @@ -581,6 +581,16 @@ public function add_admin_menu_pages() { 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1Mi4wMyA1NSI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5Bc3NldCAxPC90aXRsZT48ZyBpZD0iTGF5ZXJfMiIgZGF0YS1uYW1lPSJMYXllciAyIj48ZyBpZD0iTGF5ZXJfMS0yIiBkYXRhLW5hbWU9IkxheWVyIDEiPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTExLjY0LDI4LjU0YTQuNzUsNC43NSwwLDAsMC0xLjE3LjA4Yy0yLjc5LjU2LTQuMzYsMi45NC00LjA1LDZhNi4yNCw2LjI0LDAsMCwwLDUuNzIsNS4yMSw0LjE3LDQuMTcsMCwwLDAsLjgtLjA2YzIuODMtLjQ4LDMuNTctMy41NSwzLjEtNi41N0MxNS41MSwyOS44MywxMy4yMSwyOC42MywxMS42NCwyOC41NFptMi43Nyw4LjA3YTEuMTcsMS4xNywwLDAsMS0xLjEuNTUsMS41MywxLjUzLDAsMCwxLTEuMzctMS41OEE0LDQsMCwwLDEsMTIuMjMsMzRhMS40NCwxLjQ0LDAsMCwwLS41NS0xLjc0LDEuNDgsMS40OCwwLDAsMC0xLjEyLS4yMSwxLjQ0LDEuNDQsMCwwLDAtLjkyLjY0LDMuMzksMy4zOSwwLDAsMC0uMzQuNzlsMCwuMTFjLS4xMy4zNC0uMzMuNDUtLjQ3LjQzcy0uMTYtLjA1LS4yMS0uMjFhMywzLDAsMCwxLC43OC0yLjU1LDIuNDYsMi40NiwwLDAsMSwyLjExLS43NiwyLjUsMi41LDAsMCwxLDEuOTEsMS4zOSwzLjE5LDMuMTksMCwwLDEtLjIzLDIuODJsLS4wOS4yQTEuMTYsMS4xNiwwLDAsMCwxMywzNmEuNzQuNzQsMCwwLDAsLjYzLjMyLDEuMzgsMS4zOCwwLDAsMCwuMzQsMGMuMTUsMCwuMy0uMDcuMzksMEEuMjQuMjQsMCwwLDEsMTQuNDEsMzYuNjFaIi8+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNNTEsMzMuODhhMy44NCwzLjg0LDAsMCwwLTEuMTUtMWwtLjExLS4zNy0uMTQtLjQyYTUuNTcsNS41NywwLDAsMCwuNS0zLjMyLDUuNDMsNS40MywwLDAsMC0xLjU0LTMsMTAuMDksMTAuMDksMCwwLDAtNC4yNC0yLjI2YzAtLjY3LDAtMS40My0uMDYtMS45YTEyLjgzLDEyLjgzLDAsMCwwLS40OS0zLjI1LDEwLjQ2LDEwLjQ2LDAsMCwwLTEuMy0yLjkyYzIuMTQtMi41NiwzLjI5LTUuMjEsMy4yOS03LjU3LDAtMy44My0zLTYuMy03LjU5LTYuM2ExOS4zLDE5LjMsMCwwLDAtNy4yMiwxLjZsLS4zNC4xNEwyOC43LDEuNTJBNi4zMSw2LjMxLDAsMCwwLDI0LjQzLDAsMTQuMDcsMTQuMDcsMCwwLDAsMTcuNiwyLjJhMzYuOTMsMzYuOTMsMCwwLDAtNi43OCw1LjIxYy00LjYsNC4zOC04LjMsOS42My05LjkxLDE0QTEyLjUxLDEyLjUxLDAsMCwwLDAsMjYuNTRhNi4xNiw2LjE2LDAsMCwwLDIuMTMsNC40bC43OC42NkExMC40NCwxMC40NCwwLDAsMCwyLjc0LDM1YTkuMzYsOS4zNiwwLDAsMCwzLjIxLDYsMTAsMTAsMCwwLDAsNS4xMywyLjQzLDIwLjE5LDIwLjE5LDAsMCwwLDcuMzEsOEEyMy4zMywyMy4zMywwLDAsMCwzMC4xNyw1NUgzMWEyMy4yNywyMy4yNywwLDAsMCwxMi0zLjE2LDE5LjEsMTkuMSwwLDAsMCw3LjgyLTkuMDZsMCwwQTE2Ljg5LDE2Ljg5LDAsMCwwLDUyLDM3LjIzLDUuMTcsNS4xNywwLDAsMCw1MSwzMy44OFptLTEuNzgsOC4yMWMtMyw3LjI5LTEwLjMsMTEuMzUtMTksMTEuMDktOC4wNi0uMjQtMTQuOTQtNC41LTE4LTExLjQzYTcuOTQsNy45NCwwLDAsMS01LjEyLTIuMDYsNy41Niw3LjU2LDAsMCwxLTIuNjEtNC44NUE4LjMxLDguMzEsMCwwLDEsNSwzMUwzLjMyLDI5LjU2Qy00LjQyLDIzLDE5Ljc3LTMuODYsMjcuNTEsMi44OWwyLjY0LDIuNTgsMS40NC0uNjFjNi43OS0yLjgxLDEyLjMtMS40NSwxMi4zLDMsMCwyLjMzLTEuNDgsNS4wNS0zLjg2LDcuNTJhNy41NCw3LjU0LDAsMCwxLDIsMy40OCwxMSwxMSwwLDAsMSwuNDIsMi44MmMwLDEsLjA5LDMuMTYuMDksMy4ybDEsLjI3QTguNjQsOC42NCwwLDAsMSw0Ny4yLDI3YTMuNjYsMy42NiwwLDAsMSwxLjA2LDIuMDZBNCw0LDAsMCwxLDQ3LjU1LDMyLDEwLjE1LDEwLjE1LDAsMCwxLDQ4LDMzLjA4Yy4yLjY0LjM1LDEuMTguMzcsMS4yNS43NCwwLDEuODkuODUsMS44OSwyLjg5QTE1LjI5LDE1LjI5LDAsMCwxLDQ5LjE4LDQyLjA5WiIvPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTQ4LDM2YTEuMzYsMS4zNiwwLDAsMC0uODYtLjE2LDExLjc2LDExLjc2LDAsMCwwLS44Mi0yLjc4QTE3Ljg5LDE3Ljg5LDAsMCwxLDQwLjQ1LDM2YTIzLjY0LDIzLjY0LDAsMCwxLTcuODEuODRjLTEuNjktLjE0LTIuODEtLjYzLTMuMjMuNzRhMTguMywxOC4zLDAsMCwwLDgsLjgxLjE0LjE0LDAsMCwxLC4xNi4xMy4xNS4xNSwwLDAsMS0uMDkuMTVzLTMuMTQsMS40Ni04LjE0LS4wOGEyLjU4LDIuNTgsMCwwLDAsMS44MywxLjkxLDguMjQsOC4yNCwwLDAsMCwxLjQ0LjM5YzYuMTksMS4wNiwxMi0yLjQ3LDEzLjI3LTMuMzYuMS0uMDcuMTYsMCwuMDguMTJsLS4xMy4xOGMtMS41OSwyLjA2LTUuODgsNC40NC0xMS40NSw0LjQ0LTIuNDMsMC00Ljg2LS44Ni01Ljc1LTIuMTctMS4zOC0yLS4wNy01LDIuMjQtNC43MWwxLC4xMWEyMS4xMywyMS4xMywwLDAsMCwxMC41LTEuNjhjMy4xNS0xLjQ2LDQuMzQtMy4wNyw0LjE2LTQuMzdBMS44NywxLjg3LDAsMCwwLDQ2LDI4LjM0YTYuOCw2LjgsMCwwLDAtMy0xLjQxYy0uNS0uMTQtLjg0LS4yMy0xLjItLjM1LS42NS0uMjEtMS0uMzktMS0xLjYxLDAtLjUzLS4xMi0yLjQtLjE2LTMuMTYtLjA2LTEuMzUtLjIyLTMuMTktMS4zNi00YTEuOTIsMS45MiwwLDAsMC0xLS4zMSwxLjg2LDEuODYsMCwwLDAtLjU4LjA2LDMuMDcsMy4wNywwLDAsMC0xLjUyLjg2LDUuMjQsNS4yNCwwLDAsMS00LDEuMzJjLS44LDAtMS42NS0uMTYtMi42Mi0uMjJsLS41NywwYTUuMjIsNS4yMiwwLDAsMC01LDQuNTdjLS41NiwzLjgzLDIuMjIsNS44MSwzLDdhMSwxLDAsMCwxLC4yMi41Mi44My44MywwLDAsMS0uMjguNTVoMGE5LjgsOS44LDAsMCwwLTIuMTYsOS4yLDcuNTksNy41OSwwLDAsMCwuNDEsMS4xMmMyLDQuNzMsOC4zLDYuOTMsMTQuNDMsNC45M2ExNS4wNiwxNS4wNiwwLDAsMCwyLjMzLTEsMTIuMjMsMTIuMjMsMCwwLDAsMy41Ny0yLjY3LDEwLjYxLDEwLjYxLDAsMCwwLDMtNS44MkM0OC42LDM2LjcsNDguMzMsMzYuMjMsNDgsMzZabS04LjI1LTcuODJjMCwuNS0uMzEuOTEtLjY4LjlzLS42Ni0uNDItLjY1LS45Mi4zMS0uOTEuNjgtLjlTMzkuNzIsMjcuNjgsMzkuNzEsMjguMThabS0xLjY4LTZjLjcxLS4xMiwxLjA2LjYyLDEuMzIsMS44NWEzLjY0LDMuNjQsMCwwLDEtLjA1LDIsNC4xNCw0LjE0LDAsMCwwLTEuMDYsMCw0LjEzLDQuMTMsMCwwLDEtLjY4LTEuNjRDMzcuMjksMjMuMjMsMzcuMzEsMjIuMzQsMzgsMjIuMjNabS0yLjQsNi41N2EuODIuODIsMCwwLDEsMS4xMS0uMTljLjQ1LjIyLjY5LjY3LjUzLDFhLjgyLjgyLDAsMCwxLTEuMTEuMTlDMzUuNywyOS41OCwzNS40NywyOS4xMywzNS42MywyOC44Wm0tMi44LS4zN2MtLjA3LjExLS4yMy4wOS0uNTcuMDZhNC4yNCw0LjI0LDAsMCwwLTIuMTQuMjIsMiwyLDAsMCwxLS40OS4xNC4xNi4xNiwwLDAsMS0uMTEsMCwuMTUuMTUsMCwwLDEtLjA1LS4xMi44MS44MSwwLDAsMSwuMzItLjUxLDIuNDEsMi40MSwwLDAsMSwxLjI3LS41MywxLjk0LDEuOTQsMCwwLDEsMS43NS41N0EuMTkuMTksMCwwLDEsMzIuODMsMjguNDNabS01LjExLTEuMjZjLS4xMiwwLS4xNy0uMDctLjE5LS4xNHMuMjgtLjU2LjYyLS44MWEzLjYsMy42LDAsMCwxLDMuNTEtLjQyQTMsMywwLDAsMSwzMywyNi44N2MuMTIuMi4xNS4zNS4wNy40NHMtLjQ0LDAtLjk1LS4yNGE0LjE4LDQuMTgsMCwwLDAtMi0uNDNBMjEuODUsMjEuODUsMCwwLDAsMjcuNzEsMjcuMTdaIi8+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNMzUuNSwxMy4yOWMuMSwwLC4xNi0uMTUuMDctLjJhMTEsMTEsMCwwLDAtNC42OS0xLjIzLjA5LjA5LDAsMCwxLS4wNy0uMTQsNC43OCw0Ljc4LDAsMCwxLC44OC0uODkuMDkuMDksMCwwLDAtLjA2LS4xNiwxMi40NiwxMi40NiwwLDAsMC01LjYxLDIsLjA5LjA5LDAsMCwxLS4xMy0uMDksNi4xNiw2LjE2LDAsMCwxLC41OS0xLjQ1LjA4LjA4LDAsMCwwLS4xMS0uMTFBMjIuNzksMjIuNzksMCwwLDAsMjAsMTYuMjRhLjA5LjA5LDAsMCwwLC4xMi4xM0ExOS41MywxOS41MywwLDAsMSwyNywxMy4zMiwxOS4xLDE5LjEsMCwwLDEsMzUuNSwxMy4yOVoiLz48cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik0yOC4zNCw2LjQyUzI2LjIzLDQsMjUuNiwzLjhDMjEuNjksMi43NCwxMy4yNCw4LjU3LDcuODQsMTYuMjcsNS42NiwxOS4zOSwyLjUzLDI0LjksNCwyNy43NGExMS40MywxMS40MywwLDAsMCwxLjc5LDEuNzJBNi42NSw2LjY1LDAsMCwxLDEwLDI2Ljc4LDM0LjIxLDM0LjIxLDAsMCwxLDIwLjgsMTEuNjIsNTUuMDksNTUuMDksMCwwLDEsMjguMzQsNi40MloiLz48L2c+PC9nPjwvc3ZnPg==' ); + // Rename the auto-generated first submenu item to "Form Settings". + add_submenu_page( + 'mailchimp_sf_options', + esc_html__( 'Form Settings', 'mailchimp' ), + esc_html__( 'Form Settings', 'mailchimp' ), + MCSF_CAP_THRESHOLD, + 'mailchimp_sf_options', + array( $this, 'settings_page' ) + ); + add_submenu_page( 'admin.php', esc_html__( 'Create Mailchimp Account', 'mailchimp' ), diff --git a/includes/class-mailchimp-analytics.php b/includes/class-mailchimp-analytics.php new file mode 100644 index 0000000..acf2698 --- /dev/null +++ b/includes/class-mailchimp-analytics.php @@ -0,0 +1,116 @@ +is_connected() ) { + return; + } + + add_submenu_page( + 'mailchimp_sf_options', + esc_html__( 'Analytics', 'mailchimp' ), + esc_html__( 'Analytics', 'mailchimp' ), + MCSF_CAP_THRESHOLD, + 'mailchimp_sf_analytics', + array( $this, 'render_page' ) + ); + } + + /** + * Render the Analytics page. + */ + public function render_page() { + include_once MCSF_DIR . 'includes/admin/templates/analytics.php'; + } + + /** + * Enqueue scripts and styles only on the analytics page. + * + * @param string $hook_suffix The current admin page hook suffix. + */ + public function enqueue_scripts( $hook_suffix ) { + if ( 'mailchimp_page_mailchimp_sf_analytics' !== $hook_suffix ) { + return; + } + + wp_enqueue_style( + 'mailchimp_sf_admin_css', + MCSF_URL . 'assets/css/admin.css', + array(), + MCSF_VER + ); + + wp_enqueue_style( + 'mailchimp_sf_analytics_css', + MCSF_URL . 'assets/css/analytics.css', + array( 'mailchimp_sf_admin_css' ), + MCSF_VER + ); + + wp_enqueue_script( + 'chartjs', + 'https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js', + array(), + '4.4.7', + true + ); + + wp_enqueue_script( + 'mailchimp_sf_analytics_js', + MCSF_URL . 'assets/js/analytics.js', + array( 'chartjs' ), + MCSF_VER, + true + ); + + // Pass data to JS. + $lists = get_option( 'mailchimp_sf_lists', array() ); + $dc = get_option( 'mc_datacenter', '' ); + $current_id = get_option( 'mc_list_id', '' ); + + wp_localize_script( + 'mailchimp_sf_analytics_js', + 'mailchimpAnalytics', + array( + 'lists' => $lists, + 'currentListId' => $current_id, + 'dataCenter' => $dc, + ) + ); + } +} diff --git a/mailchimp.php b/mailchimp.php index caf1cd2..ea07331 100644 --- a/mailchimp.php +++ b/mailchimp.php @@ -110,6 +110,11 @@ function () { $form_submission = new Mailchimp_Form_Submission(); $form_submission->init(); +// Init Analytics page. +require_once plugin_dir_path( __FILE__ ) . 'includes/class-mailchimp-analytics.php'; +$analytics = new Mailchimp_Analytics(); +$analytics->init(); + // Deprecated functions. require_once plugin_dir_path( __FILE__ ) . 'includes/mailchimp-deprecated-functions.php';