Changeset 3286617
- Timestamp:
- 05/03/2025 03:56:09 AM (11 months ago)
- Location:
- dashcommerce/trunk
- Files:
-
- 6 added
- 8 edited
-
assets/js/img (added)
-
assets/js/img/flags.webp (added)
-
assets/js/intl-tel-input (added)
-
assets/js/intl-tel-input/intlTelInput.css (added)
-
assets/js/intl-tel-input/intlTelInput.min.js (added)
-
assets/js/intl-tel-input/intlTelInputWithUtils.min.js (added)
-
dashcommerce.php (modified) (1 diff)
-
features/diagnostics/class-diagnostics.php (modified) (5 diffs)
-
features/diagnostics/diagnostics.js (modified) (5 diffs)
-
features/reports/class-reports.php (modified) (4 diffs)
-
features/reports/script-reports.js (modified) (10 diffs)
-
readme.txt (modified) (1 diff)
-
utils/class-utils.php (modified) (1 diff)
-
utils/script-utils.js (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
dashcommerce/trunk/dashcommerce.php
r3276913 r3286617 12 12 * Plugin Name: DashCommerce - Support, Checkup, Optimization, AI, Reports & Analytics 13 13 * Description: Keep your website healthy and efficient with DashCommerce. 14 * Version: 1.3. 314 * Version: 1.3.4 15 15 * Author: DashCommerce 16 16 * License: GPL v2 -
dashcommerce/trunk/features/diagnostics/class-diagnostics.php
r3275268 r3286617 128 128 wp_enqueue_script( 'luxon', plugin_dir_url( __FILE__ ) . '../../assets/js/luxon.min.js', array(), '3.5.0', true ); 129 129 wp_enqueue_script( 'luxon-adapter', plugin_dir_url( __FILE__ ) . '../../assets/js/chartjs-adapter-luxon.umd.js', array(), '1.3.1', true ); 130 131 wp_enqueue_script( 'iti', plugin_dir_url( __FILE__ ) . '../../assets/js/intl-tel-input/intlTelInputWithUtils.min.js', array(), '25.3.1', true ); 132 wp_script_add_data( 'iti', 'type', 'module' ); 133 134 wp_enqueue_style( 'iti-styles', plugin_dir_url( __FILE__ ) . '../../assets/js/intl-tel-input/intlTelInput.css', array(), '25.3.1' ); 130 135 131 136 wp_enqueue_script( 'dashcommerce-diagnostics-js', plugin_dir_url( __FILE__ ) . 'diagnostics.js', array( 'jquery', 'dashcommerce-utils-js' ), $ver, true ); … … 148 153 'scheduled' => $this->cron_schedule(), 149 154 'req_phone' => ! $this->account->get_phone(), 155 'country' => $this->utils->get_site_country(), 150 156 ) 151 157 ); … … 219 225 220 226 <div style="margin: 10px 0;"> 221 <input type="tel" id="dashcommerce-diagnostics-phone-modal-input" name="phone" placeholder="(XX) XXXXX-XXXX">227 <input type="tel" id="dashcommerce-diagnostics-phone-modal-input"> 222 228 </div> 223 229 … … 227 233 </div> 228 234 229 <button id="dashcommerce-diagnostics-phone-modal-continue" class="button dashcommerce-button" >235 <button id="dashcommerce-diagnostics-phone-modal-continue" class="button dashcommerce-button" disabled="disabled"> 230 236 Gerar diagnóstico 231 237 </button> … … 791 797 $phone = isset( $_POST['phone'] ) ? sanitize_text_field( wp_unslash( $_POST['phone'] ) ) : null; 792 798 799 $phone = preg_replace( '/\D/', '', $phone ); 800 793 801 if ( is_null( $phone ) ) { 794 802 wp_send_json( -
dashcommerce/trunk/features/diagnostics/diagnostics.js
r3275268 r3286617 48 48 continue: jQuery('#dashcommerce-diagnostics-phone-modal-continue'), 49 49 saving: jQuery('#dashcommerce-diagnostics-phone-modal-spinner-saving'), 50 inputITI: null, 50 51 } 51 52 } … … 103 104 }, 104 105 savePhone: async (phone) => { 105 if (!phone || !dashcommerce_utils.validateBrPhone(phone)) {106 throw new Error('Please enter a valid phone number.');107 }108 109 106 this.setSaving(true); 110 107 … … 226 223 227 224 this.elements.modals.phone.continue.on('click', async () => { 228 const phone = this.elements.modals.phone.input .val();225 const phone = this.elements.modals.phone.inputITI.getNumber(window['intlTelInput'].utils.numberFormat.INTERNATIONAL); 229 226 230 227 if (typeof phone !== 'string' || phone.length === 0) { … … 233 230 } 234 231 232 const isValid = this.elements.modals.phone.inputITI.isValidNumber(); 233 234 if (!isValid) { 235 const errorCode = this.elements.modals.phone.inputITI.getValidationError(); 236 237 alert(`Please enter a valid phone number.`); 238 239 console.error(`[DashCommerce] Phone number '${phone} 'is invalid: code ${errorCode}`); 240 241 return; 242 } 243 235 244 try { 236 245 this.elements.modals.phone.saving.show(); 246 this.elements.modals.phone.continue.attr('disabled', 'disabled'); 247 237 248 await this.requests.savePhone(phone); 238 this.elements.modals.phone.saving.hide();239 249 } 240 250 catch (error) { 241 this.elements.modals.phone.saving.hide();242 251 alert(error.message); 243 252 return; 253 } 254 finally { 255 this.elements.modals.phone.saving.hide(); 256 this.elements.modals.phone.continue.removeAttr('disabled'); 244 257 } 245 258 … … 296 309 297 310 this.charts.line('scoreHistoryLineChart', dashcommerce_vars_diagnostics?.history); 311 312 this.elements.modals.phone.inputITI = dashcommerce_utils.initializeItiField( 313 this.elements.modals.phone.input.get(0), 314 dashcommerce_vars_diagnostics?.country, 315 this.elements.modals.phone.continue.get(0) 316 ); 298 317 } 299 318 -
dashcommerce/trunk/features/reports/class-reports.php
r3275268 r3286617 60 60 $ver = $dashcommerce_utils->get_plugin_version(); 61 61 62 wp_enqueue_script( 'iti', plugin_dir_url( __FILE__ ) . '../../assets/js/intl-tel-input/intlTelInputWithUtils.min.js', array(), '25.3.1', true ); 63 wp_script_add_data( 'iti', 'type', 'module' ); 64 65 wp_enqueue_style( 'iti-styles', plugin_dir_url( __FILE__ ) . '../../assets/js/intl-tel-input/intlTelInput.css', array(), '25.3.1' ); 66 62 67 wp_enqueue_script( 'dashcommerce-reports-page-js', plugin_dir_url( __FILE__ ) . 'script-reports.js', array( 'jquery', 'dashcommerce-utils-js' ), $ver, true ); 63 68 … … 69 74 'nonce' => wp_create_nonce( 'dashcommerce_nonce' ), 70 75 'settings' => $dashcommerce_settings->get_settings(), 76 'country' => $this->utils->get_site_country(), 71 77 ) 72 78 ); … … 183 189 <div> 184 190 <label>Números de WhatsApp:</label> 185 <input style="margin-right: 10px;" type="tel" id="dashcommerce-reports-input-mobile-1" placeholder="Adicione um número"> 186 <input style="margin-right: 10px;" type="tel" id="dashcommerce-reports-input-mobile-2" placeholder="Adicione um número"> 187 <input type="tel" id="dashcommerce-reports-input-mobile-3" placeholder="Adicione um número"> 188 (somente números, com código do país, DDD e dígito 9 - ex: 5561912345678) 191 <input style="margin-right: 10px;" type="tel" id="dashcommerce-reports-input-mobile-1"> 192 <input style="margin-right: 10px;" type="tel" id="dashcommerce-reports-input-mobile-2"> 193 <input type="tel" id="dashcommerce-reports-input-mobile-3"> 189 194 </div> 190 195 … … 419 424 $topics = $report_settings['topics']; 420 425 426 $message = $this->generator->generate( 'daily', $store_name, $topics ); 427 421 428 if ( isset( $mobiles['mobile1'] ) && ! empty( $mobiles['mobile1'] ) ) { 422 $message = $this->generator->generate( 'daily', $store_name, $topics );423 424 429 $this->send_whatsapp_message( $mobiles['mobile1'], $message ); 425 430 } 426 431 427 432 if ( isset( $mobiles['mobile2'] ) && ! empty( $mobiles['mobile2'] ) ) { 428 $message = $this->generator->generate( 'daily', $store_name, $topics );429 430 433 $this->send_whatsapp_message( $mobiles['mobile2'], $message ); 431 434 } 432 435 433 436 if ( isset( $mobiles['mobile3'] ) && ! empty( $mobiles['mobile3'] ) ) { 434 $message = $this->generator->generate( 'daily', $store_name, $topics );435 436 437 $this->send_whatsapp_message( $mobiles['mobile3'], $message ); 437 438 } 438 439 439 440 if ( isset( $webhook ) && ! empty( $webhook ) ) { 440 $message = $this->generator->generate( 'daily', $store_name, $topics );441 442 441 $this->send_webhook_call( $webhook, $message ); 443 442 } -
dashcommerce/trunk/features/reports/script-reports.js
r3275268 r3286617 33 33 constructor() { 34 34 this.elements.inputs.all.on('change input', async (event) => { 35 this.toggle Save();35 this.toggleCanSaveOrSend(); 36 36 }); 37 37 … … 50 50 51 51 dashcommerce_utils.finishLoading(); 52 53 52 } 54 53 … … 63 62 preferredTime: jQuery('#dashcommerce-reports-input-preferred-time'), 64 63 mobile1: jQuery('#dashcommerce-reports-input-mobile-1'), 64 mobile1iti: null, 65 65 mobile2: jQuery('#dashcommerce-reports-input-mobile-2'), 66 mobile2iti: null, 66 67 mobile3: jQuery('#dashcommerce-reports-input-mobile-3'), 68 mobile3iti: null, 67 69 webhook: jQuery('#dashcommerce-reports-input-webhook'), 68 70 includeAccessCount: jQuery('#dashcommerce-reports-input-optional-accesses'), … … 168 170 }, 169 171 mobiles: { 170 mobile1: this.elements.inputs.mobile1 .val().toString() || null,171 mobile2: this.elements.inputs.mobile2 .val().toString() || null,172 mobile3: this.elements.inputs.mobile3 .val().toString() || null,172 mobile1: this.elements.inputs.mobile1iti.getNumber(window['intlTelInput'].utils.numberFormat.INTERNATIONAL)?.replace(/\D/g, '') || null, 173 mobile2: this.elements.inputs.mobile2iti.getNumber(window['intlTelInput'].utils.numberFormat.INTERNATIONAL)?.replace(/\D/g, '') || null, 174 mobile3: this.elements.inputs.mobile3iti.getNumber(window['intlTelInput'].utils.numberFormat.INTERNATIONAL)?.replace(/\D/g, '') || null, 173 175 }, 174 176 time: dashcommerce_utils.hoursMinutesToGMT(this.elements.inputs.preferredTime.val()), … … 205 207 */ 206 208 fill(settings) { 207 if (!settings) return; 208 209 this.savedSettings = settings; 210 211 this.elements.inputs.dailyEnable.prop('checked', settings.schedules.daily.enable); 212 this.elements.inputs.weeklyEnable.prop('checked', settings.schedules.weekly.enable); 213 this.elements.inputs.monthlyEnable.prop('checked', settings.schedules.monthly.enable); 214 215 this.elements.inputs.weeklyWeekday.val(settings.schedules.weekly.weekday); 216 217 this.elements.inputs.preferredTime.val(dashcommerce_utils.hoursMinutesToLocal(settings.time)); 218 219 if (!settings.time) { 220 this.elements.inputs.preferredTime.val('08:00'); 221 } 209 this.elements.inputs.mobile1iti?.destroy(); 210 this.elements.inputs.mobile2iti?.destroy(); 211 this.elements.inputs.mobile3iti?.destroy(); 222 212 223 213 this.elements.inputs.mobile1.val(settings.mobiles.mobile1); … … 225 215 this.elements.inputs.mobile3.val(settings.mobiles.mobile3); 226 216 227 this.elements.inputs.webhook.val(settings.webhook); 228 229 if (settings.topics) { 230 this.elements.inputs.includeAccessCount.prop('checked', settings.topics.includes('access_count')); 231 this.elements.inputs.includeSalesCount.prop('checked', settings.topics.includes('sales_count')); 232 this.elements.inputs.includeIncome.prop('checked', settings.topics.includes('income')); 233 this.elements.inputs.includeUtm.prop('checked', settings.topics.includes('utm')); 234 this.elements.inputs.includeMostAccessedPages.prop('checked', settings.topics.includes('most_acessed_pages')); 235 this.elements.inputs.includeMostViewedProds.prop('checked', settings.topics.includes('most_viewed_prods')); 236 this.elements.inputs.includeMostSoldProducts.prop('checked', settings.topics.includes('most_sold_prods')); 237 } 238 239 this.elements.actions.save.attr('disabled', 'disabled'); 240 241 this.toggleSave(); 217 this.elements.inputs.mobile1iti = dashcommerce_utils.initializeItiField( 218 this.elements.inputs.mobile1.get(0), 219 dashcommerce_vars_reports?.country, 220 ); 221 222 this.elements.inputs.mobile2iti = dashcommerce_utils.initializeItiField( 223 this.elements.inputs.mobile2.get(0), 224 dashcommerce_vars_reports?.country, 225 ); 226 227 this.elements.inputs.mobile3iti = dashcommerce_utils.initializeItiField( 228 this.elements.inputs.mobile3.get(0), 229 dashcommerce_vars_reports?.country, 230 ); 231 232 this.savedSettings = settings; 233 234 this.elements.inputs.dailyEnable.prop('checked', settings?.schedules.daily.enable); 235 this.elements.inputs.weeklyEnable.prop('checked', settings?.schedules.weekly.enable); 236 this.elements.inputs.monthlyEnable.prop('checked', settings?.schedules.monthly.enable); 237 238 this.elements.inputs.weeklyWeekday.val(settings?.schedules.weekly.weekday); 239 240 this.elements.inputs.preferredTime.val(dashcommerce_utils.hoursMinutesToLocal(settings?.time)); 241 242 if (!settings?.time) { 243 this.elements.inputs.preferredTime.val('08:00'); 244 } 245 246 this.elements.inputs.webhook.val(settings?.webhook); 247 248 if (settings?.topics) { 249 this.elements.inputs.includeAccessCount.prop('checked', settings?.topics.includes('access_count')); 250 this.elements.inputs.includeSalesCount.prop('checked', settings?.topics.includes('sales_count')); 251 this.elements.inputs.includeIncome.prop('checked', settings?.topics.includes('income')); 252 this.elements.inputs.includeUtm.prop('checked', settings?.topics.includes('utm')); 253 this.elements.inputs.includeMostAccessedPages.prop('checked', settings?.topics.includes('most_acessed_pages')); 254 this.elements.inputs.includeMostViewedProds.prop('checked', settings?.topics.includes('most_viewed_prods')); 255 this.elements.inputs.includeMostSoldProducts.prop('checked', settings?.topics.includes('most_sold_prods')); 256 } 257 258 this.toggleCanSaveOrSend(); 242 259 } 243 260 244 261 setSaving(isSaving) { 245 262 if (isSaving === true) { 263 this.elements.actions.save.attr('disabled', 'disabled'); 246 264 this.elements.spinners.saving.show(); 247 265 this.elements.all.attr('disabled', 'disabled'); … … 250 268 this.elements.spinners.saving.hide(); 251 269 this.elements.all.removeAttr('disabled'); 252 } 253 254 this.toggleSave(); 255 256 if (isSaving === true) { 257 this.elements.actions.save.attr('disabled', 'disabled'); 258 this.elements.actions.send.attr('disabled', 'disabled'); 259 } 260 else { 261 this.elements.actions.send.removeAttr('disabled'); 270 271 this.toggleCanSaveOrSend(); 262 272 } 263 273 } … … 265 275 setSending(isSending) { 266 276 if (isSending === true) { 277 this.elements.actions.send.attr('disabled', 'disabled'); 267 278 this.elements.spinners.sending.show(); 268 279 this.elements.all.attr('disabled', 'disabled'); 269 this.elements.actions.send.attr('disabled', 'disabled');270 280 } 271 281 else { 272 282 this.elements.spinners.sending.hide(); 273 283 this.elements.all.removeAttr('disabled'); 274 this.elements.actions.send.removeAttr('disabled'); 275 } 276 277 this.toggleSave(); 278 279 if (isSending === true) { 280 this.elements.actions.send.attr('disabled', 'disabled'); 281 } 282 else { 283 this.elements.actions.send.removeAttr('disabled'); 284 } 285 } 286 287 toggleSave() { 284 285 this.toggleCanSaveOrSend(); 286 } 287 } 288 289 validateMobileNumbers() { 290 const validateMobileNumber = (itiInput) => itiInput.isValidNumber() || itiInput.getNumber().length === 0; 291 292 const mobile1ok = validateMobileNumber(this.elements.inputs.mobile1iti); 293 const mobile2ok = validateMobileNumber(this.elements.inputs.mobile2iti); 294 const mobile3ok = validateMobileNumber(this.elements.inputs.mobile3iti); 295 296 return mobile1ok && mobile2ok && mobile3ok; 297 } 298 299 toggleCanSaveOrSend() { 288 300 let dailyOk = true, weeklyOk = true, monthlyOk = true, mobilesOk = true; 289 301 290 302 const isTimeSelected = this.elements.inputs.preferredTime.val(); 291 303 const isWeekdaySelected = this.elements.inputs.weeklyWeekday?.val() || null; 292 const noMobileNumbers = !this.elements.inputs.mobile1.val() && !this.elements.inputs.mobile2.val() && !this.elements.inputs.mobile3.val();293 304 294 305 if (this.elements.inputs.dailyEnable.prop('checked') && !isTimeSelected) { … … 304 315 } 305 316 306 if ( noMobileNumbers) {317 if (!this.validateMobileNumbers()) { 307 318 mobilesOk = false; 308 319 } … … 310 321 const settingsAreValid = dailyOk && weeklyOk && monthlyOk && mobilesOk; 311 322 312 const okToSave = settingsAreValid && !dashcommerce_utils.deepEqual(this.savedSettings, this.getDisplayedSettings()); 323 const savedAndCurrentAreEqual = dashcommerce_utils.deepEqual(this.savedSettings, this.getDisplayedSettings()); 324 325 const okToSave = settingsAreValid && !savedAndCurrentAreEqual; 313 326 314 327 if (okToSave) { 315 328 this.elements.actions.save.removeAttr('disabled'); 316 this.elements.actions.send.attr('disabled', 'disabled');329 console.log('can save', dailyOk, weeklyOk, monthlyOk, mobilesOk, settingsAreValid) 317 330 } 318 331 else { 319 332 this.elements.actions.save.attr('disabled', 'disabled'); 333 console.log('cannot save'); 334 } 335 336 const okToSend = savedAndCurrentAreEqual; 337 338 if (okToSend) { 320 339 this.elements.actions.send.removeAttr('disabled'); 340 console.log('can send'); 341 } 342 else { 343 this.elements.actions.send.attr('disabled', 'disabled'); 344 console.log('cannot send') 321 345 } 322 346 } -
dashcommerce/trunk/readme.txt
r3276913 r3286617 4 4 Requires at least: WordPress 5.0 5 5 Tested up to: 6.8 6 Stable tag: 1.3. 36 Stable tag: 1.3.4 7 7 Requires PHP: 7.0.0 8 8 License: GPL v2 -
dashcommerce/trunk/utils/class-utils.php
r3275268 r3286617 941 941 return $admin_info; 942 942 } 943 944 /** 945 * Get the site's country code based on the locale. 946 * 947 * @return string|null The country code in uppercase (e.g., 'BR', 'US', 'IT') or null if not set. 948 */ 949 public function get_site_country() { 950 $locale = get_option( 'WPLANG' ); 951 952 if ( empty( $locale ) ) { 953 return null; 954 } 955 956 $locale_parts = explode( '_', $locale ); 957 958 if ( ! isset( $locale_parts[1] ) ) { 959 return null; 960 } 961 962 return strtoupper( $locale_parts[1] ); 963 } 943 964 } 944 965 -
dashcommerce/trunk/utils/script-utils.js
r3275268 r3286617 177 177 178 178 /** 179 * Validates a Brazilian phone number.180 *181 * @param {string} phone182 */183 validateBrPhone: (phone) => {184 phone = phone.replace(/\D/g, '');185 186 const regex = /^55(?:\d{2})[\s\-\(\)\.]*(?:9?\d{8})[\s\-\(\)\.]*$/;187 188 return regex.test(phone);189 },190 191 /**192 179 * Downloads a base64 file via the user's browser. 193 180 * … … 256 243 a.click(); 257 244 URL.revokeObjectURL(url); 258 } 245 }, 246 247 /** 248 * Initialize the International Telephone Input (ITI) field. 249 * The intl-tel-input library must be included in the page (and present in `window`) for this to work. 250 * 251 * @param {HTMLElement} element - The input element to initialize. 252 * @param {string | undefined} initialCountry - The initial country code. Defaults to `'US'` 253 * @param {HTMLElement | undefined=} dependentButton - A button element that will be enabled/disabled based on the validity of the phone number. 254 */ 255 initializeItiField: (element, initialCountry, dependentButton) => { 256 const iti = window['intlTelInput'](element, { 257 initialCountry: initialCountry || 'US', 258 separateDialCode: true, 259 showFlags: true, 260 strictMode: true 261 }); 262 263 element.addEventListener('input', () => { 264 if (iti.isValidNumber()) { 265 element.style.borderColor = "unset"; 266 267 dependentButton?.removeAttribute('disabled'); 268 } 269 else { 270 element.style.borderColor = "red"; 271 272 dependentButton?.setAttribute('disabled', 'disabled'); 273 } 274 }); 275 276 return iti; 277 }, 259 278 };
Note: See TracChangeset
for help on using the changeset viewer.