Analytics.js
263 lines
| 1 | /** |
| 2 | * External Dependencies |
| 3 | */ |
| 4 | import { applyFilters } from '@wordpress/hooks'; |
| 5 | import { __ } from '@wordpress/i18n'; |
| 6 | import { useEffect, useState } from 'react'; |
| 7 | |
| 8 | import analyticsPreview from '../../images/analytics-preview.png'; |
| 9 | import './main.scss'; |
| 10 | |
| 11 | const CrownIcon = () => ( |
| 12 | <svg |
| 13 | xmlns="http://www.w3.org/2000/svg" |
| 14 | viewBox="0 0 24 24" |
| 15 | fill="none" |
| 16 | stroke="currentColor" |
| 17 | strokeWidth="2" |
| 18 | strokeLinecap="round" |
| 19 | strokeLinejoin="round" |
| 20 | > |
| 21 | <path d="M11.562 3.266a.5.5 0 0 1 .876 0L15.39 8.87a1 1 0 0 0 1.516.294L21.183 5.5a.5.5 0 0 1 .798.519l-2.834 10.246a1 1 0 0 1-.956.734H5.81a1 1 0 0 1-.957-.734L2.02 6.02a.5.5 0 0 1 .798-.519l4.276 3.664a1 1 0 0 0 1.516-.294z" /> |
| 22 | <path d="M5 21h14" /> |
| 23 | </svg> |
| 24 | ); |
| 25 | |
| 26 | const BarChartIcon = () => ( |
| 27 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> |
| 28 | <line x1="18" y1="20" x2="18" y2="10" /> |
| 29 | <line x1="12" y1="20" x2="12" y2="4" /> |
| 30 | <line x1="6" y1="20" x2="6" y2="14" /> |
| 31 | </svg> |
| 32 | ); |
| 33 | |
| 34 | const DownloadIcon = () => ( |
| 35 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> |
| 36 | <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /> |
| 37 | <polyline points="7 10 12 15 17 10" /> |
| 38 | <line x1="12" y1="15" x2="12" y2="3" /> |
| 39 | </svg> |
| 40 | ); |
| 41 | |
| 42 | const PrinterIcon = () => ( |
| 43 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> |
| 44 | <polyline points="6 9 6 2 18 2 18 9" /> |
| 45 | <path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2" /> |
| 46 | <rect x="6" y="14" width="12" height="8" /> |
| 47 | </svg> |
| 48 | ); |
| 49 | |
| 50 | const UpgradeModal = ( { onClose, upgradeURL } ) => ( |
| 51 | <div className="EVF-Free-Analytics__ModalBackdrop" onClick={ onClose }> |
| 52 | <div className="EVF-Free-Analytics__ModalBox" onClick={ ( e ) => e.stopPropagation() }> |
| 53 | <button className="EVF-Free-Analytics__ModalClose" onClick={ onClose } aria-label="Close"> |
| 54 | × |
| 55 | </button> |
| 56 | <h3 className="EVF-Free-Analytics__OverlayTitle"> |
| 57 | { __( 'Unlock Export & Print', 'everest-forms' ) } |
| 58 | </h3> |
| 59 | <p className="EVF-Free-Analytics__OverlayText"> |
| 60 | { __( |
| 61 | 'Export your analytics data as CSV and print detailed reports with submission tracking, form insights, and conversion analysis.', |
| 62 | 'everest-forms', |
| 63 | ) } |
| 64 | </p> |
| 65 | <a href={ upgradeURL } className="EVF-Free-Analytics__UpgradeBtn" target="_blank" rel="noopener noreferrer"> |
| 66 | <CrownIcon /> |
| 67 | { __( 'Upgrade to Pro', 'everest-forms' ) } |
| 68 | </a> |
| 69 | </div> |
| 70 | </div> |
| 71 | ); |
| 72 | |
| 73 | const ChevronDownIcon = () => ( |
| 74 | <svg |
| 75 | xmlns="http://www.w3.org/2000/svg" |
| 76 | viewBox="0 0 24 24" |
| 77 | fill="none" |
| 78 | stroke="#6b7280" |
| 79 | strokeWidth="2" |
| 80 | strokeLinecap="round" |
| 81 | strokeLinejoin="round" |
| 82 | > |
| 83 | <polyline points="6 9 12 15 18 9" /> |
| 84 | </svg> |
| 85 | ); |
| 86 | |
| 87 | const getStaticDateRange = () => { |
| 88 | const now = new Date(); |
| 89 | const from = new Date(now); |
| 90 | from.setDate(from.getDate() - 30); |
| 91 | const fmt = (d) => |
| 92 | d.toLocaleDateString('en-US', { |
| 93 | month: 'short', |
| 94 | day: 'numeric', |
| 95 | year: 'numeric', |
| 96 | }); |
| 97 | return `${fmt(from)} – ${fmt(now)}`; |
| 98 | }; |
| 99 | |
| 100 | const METRIC_BOXES = [ |
| 101 | { label: __('Total Submissions', 'everest-forms') }, |
| 102 | { label: __('Complete Submissions', 'everest-forms') }, |
| 103 | { label: __('Incomplete Submissions', 'everest-forms') }, |
| 104 | { label: __('Impressions', 'everest-forms') }, |
| 105 | ]; |
| 106 | |
| 107 | /** |
| 108 | * FreeAnalyticsContent |
| 109 | * |
| 110 | * Shows a non-interactive header (filters + metric boxes showing 0) that |
| 111 | * mirrors the Pro analytics UI, then a blurred Pro screenshot with an |
| 112 | * "Unlock Advanced Analytics" upgrade overlay. |
| 113 | * |
| 114 | * All styles live in ./main.scss under .EVF-Free-Analytics. |
| 115 | * When the Pro plugin is active it replaces this entirely via the |
| 116 | * `everest-forms-analytics` WordPress filter. |
| 117 | */ |
| 118 | const FreeAnalyticsContent = () => { |
| 119 | const [ showModal, setShowModal ] = useState( false ); |
| 120 | |
| 121 | const upgradeURL = |
| 122 | typeof _EVF_DASHBOARD_ !== 'undefined' && _EVF_DASHBOARD_.upgradeURL |
| 123 | ? `${_EVF_DASHBOARD_.upgradeURL}utm_medium=evf-dashboard&utm_source=evf-free&utm_campaign=analytics-upgrade-btn&utm_content=Upgrade+to+Pro` |
| 124 | : 'https://everestforms.net/pricing/?utm_medium=evf-dashboard&utm_source=evf-free&utm_campaign=analytics-upgrade-btn&utm_content=Upgrade+to+Pro'; |
| 125 | |
| 126 | const dateRange = getStaticDateRange(); |
| 127 | |
| 128 | const openModal = ( e ) => { |
| 129 | e.preventDefault(); |
| 130 | setShowModal( true ); |
| 131 | }; |
| 132 | |
| 133 | return ( |
| 134 | <div className="EVF-Free-Analytics"> |
| 135 | { showModal && ( |
| 136 | <UpgradeModal |
| 137 | onClose={ () => setShowModal( false ) } |
| 138 | upgradeURL={ upgradeURL } |
| 139 | /> |
| 140 | ) } |
| 141 | <div className="EVF-Free-Analytics__Filters"> |
| 142 | <div> |
| 143 | <button |
| 144 | className="EVF-Free-Analytics__FilterTrigger" |
| 145 | disabled |
| 146 | tabIndex={-1} |
| 147 | > |
| 148 | {__('All Forms', 'everest-forms')} |
| 149 | <ChevronDownIcon /> |
| 150 | </button> |
| 151 | </div> |
| 152 | <div> |
| 153 | <button |
| 154 | className="EVF-Free-Analytics__FilterTrigger" |
| 155 | disabled |
| 156 | tabIndex={-1} |
| 157 | > |
| 158 | <span>{dateRange}</span> |
| 159 | <ChevronDownIcon /> |
| 160 | </button> |
| 161 | </div> |
| 162 | <div> |
| 163 | <button |
| 164 | className="EVF-Free-Analytics__FilterTrigger" |
| 165 | disabled |
| 166 | tabIndex={-1} |
| 167 | > |
| 168 | {__('Day', 'everest-forms')} |
| 169 | <ChevronDownIcon /> |
| 170 | </button> |
| 171 | </div> |
| 172 | <div className="EVF-Free-Analytics__Actions"> |
| 173 | <button className="EVF-Free-Analytics__ActionBtn" onClick={ openModal }> |
| 174 | <DownloadIcon /> |
| 175 | { __( 'Export', 'everest-forms' ) } |
| 176 | <CrownIcon /> |
| 177 | </button> |
| 178 | <button className="EVF-Free-Analytics__ActionBtn" onClick={ openModal }> |
| 179 | <PrinterIcon /> |
| 180 | { __( 'Print', 'everest-forms' ) } |
| 181 | <CrownIcon /> |
| 182 | </button> |
| 183 | </div> |
| 184 | </div> |
| 185 | |
| 186 | <div className="EVF-Free-Analytics__Metrics"> |
| 187 | {METRIC_BOXES.map((metric) => ( |
| 188 | <div key={metric.label} className="EVF-Free-Analytics__Metric"> |
| 189 | <div className="EVF-Free-Analytics__MetricHeader"> |
| 190 | <button |
| 191 | className="EVF-Free-Analytics__MetricLabel" |
| 192 | disabled |
| 193 | tabIndex={-1} |
| 194 | > |
| 195 | <span>{metric.label}</span> |
| 196 | </button> |
| 197 | </div> |
| 198 | <div className="EVF-Free-Analytics__MetricValue"> |
| 199 | 0<span className="EVF-Free-Analytics__MetricDelta">▲ 0%</span> |
| 200 | </div> |
| 201 | <div className="EVF-Free-Analytics__MetricComparison"> |
| 202 | {__('vs. 0 last period', 'everest-forms')} |
| 203 | </div> |
| 204 | </div> |
| 205 | ))} |
| 206 | </div> |
| 207 | |
| 208 | <div className="EVF-Free-Analytics__Preview"> |
| 209 | <img |
| 210 | className="EVF-Free-Analytics__PreviewImage" |
| 211 | src={analyticsPreview} |
| 212 | alt="" |
| 213 | /> |
| 214 | <div className="EVF-Free-Analytics__Overlay"> |
| 215 | <h3 className="EVF-Free-Analytics__OverlayTitle"> |
| 216 | {__('Unlock Advanced Analytics', 'everest-forms')} |
| 217 | </h3> |
| 218 | <p className="EVF-Free-Analytics__OverlayText"> |
| 219 | {__( |
| 220 | 'Get powerful analytics with submission tracking, form insights, conversion analysis, and advanced visualizations.', |
| 221 | 'everest-forms', |
| 222 | )} |
| 223 | </p> |
| 224 | <a href={upgradeURL} className="EVF-Free-Analytics__UpgradeBtn" > |
| 225 | <CrownIcon /> |
| 226 | {__('Upgrade to Pro', 'everest-forms')} |
| 227 | </a> |
| 228 | </div> |
| 229 | </div> |
| 230 | </div> |
| 231 | ); |
| 232 | }; |
| 233 | |
| 234 | /** |
| 235 | * Analytics Screen Component |
| 236 | * |
| 237 | * Uses a WordPress filter so the Pro plugin can inject its own analytics UI. |
| 238 | * Falls back to FreeAnalyticsContent for free users. |
| 239 | */ |
| 240 | const Analytics = () => { |
| 241 | const [isReady, setIsReady] = useState(false); |
| 242 | |
| 243 | useEffect(() => { |
| 244 | const timer = setTimeout(() => { |
| 245 | setIsReady(true); |
| 246 | }, 0); |
| 247 | return () => clearTimeout(timer); |
| 248 | }, []); |
| 249 | |
| 250 | const AnalyticsContent = applyFilters( |
| 251 | 'everest-forms-analytics', |
| 252 | FreeAnalyticsContent, |
| 253 | ); |
| 254 | |
| 255 | if (!isReady) { |
| 256 | return null; |
| 257 | } |
| 258 | |
| 259 | return <AnalyticsContent />; |
| 260 | }; |
| 261 | |
| 262 | export default Analytics; |
| 263 |