Plugin Directory

Changeset 3548033


Ignore:
Timestamp:
05/25/2026 06:02:34 PM (5 months ago)
Author:
smsreminder
Message:

V1.1.0 - Marketing and education updates for optional SMS reminders. No settings changes or reconnection required.

Location:
smsreminder-appointment-booking
Files:
41 added
10 edited

Legend:

Unmodified
Added
Removed
  • smsreminder-appointment-booking/trunk/assets/admin-booking-settings.css

    r3519533 r3548033  
    245245        }
    246246}
     247
     248.srb-reminders-column-header {
     249        display: inline-flex;
     250        align-items: center;
     251        gap: 4px;
     252}
     253
     254.srb-help-tip-wrap {
     255        position: relative;
     256        display: inline-flex;
     257        vertical-align: middle;
     258}
     259
     260.srb-help-tip {
     261        border: none;
     262        background: transparent;
     263        padding: 0;
     264        margin: 0;
     265        width: 20px;
     266        height: 20px;
     267        line-height: 20px;
     268        font-size: 20px;
     269        color: #646970;
     270        cursor: help;
     271        vertical-align: middle;
     272}
     273
     274.srb-help-tip:hover,
     275.srb-help-tip:focus {
     276        color: #2271b1;
     277}
     278
     279.srb-help-tip:focus {
     280        outline: 2px solid #2271b1;
     281        outline-offset: 1px;
     282        box-shadow: none;
     283}
     284
     285.srb-card table.widefat {
     286        overflow: visible;
     287}
     288
     289.srb-card table.widefat thead th {
     290        overflow: visible;
     291        position: relative;
     292}
     293
     294.srb-help-tip-popover {
     295        position: absolute;
     296        z-index: 100;
     297        top: calc(100% + 6px);
     298        left: 50%;
     299        transform: translateX(-50%);
     300        min-width: 220px;
     301        max-width: min(280px, calc(100vw - 32px));
     302        padding: 12px 14px;
     303        background: #fff;
     304        border: 1px solid #c3c4c7;
     305        border-radius: 4px;
     306        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
     307        text-align: left;
     308        font-weight: 400;
     309        font-size: 13px;
     310        line-height: 1.45;
     311        color: #1d2327;
     312        white-space: normal;
     313}
     314
     315.srb-help-tip-popover[hidden] {
     316        display: none;
     317}
     318
     319.srb-help-tip-popover__title {
     320        margin: 0 0 6px;
     321}
     322
     323.srb-help-tip-popover__body {
     324        margin: 0 0 8px;
     325}
     326
     327.srb-help-tip-popover__action {
     328        margin: 0;
     329}
     330
     331@media (max-width: 782px) {
     332        .srb-help-tip-popover {
     333                left: auto;
     334                right: 0;
     335                transform: none;
     336        }
     337}
     338
     339.srb-reminders-promo {
     340        background: #fff8e5;
     341        border-color: #f0e6c8;
     342        color: #1d2327;
     343}
     344
     345.srb-reminders-promo__header {
     346        display: flex;
     347        justify-content: space-between;
     348        align-items: flex-start;
     349        gap: 16px;
     350}
     351
     352.srb-reminders-promo__header h2 {
     353        flex: 1 1 auto;
     354        margin-bottom: 0;
     355}
     356
     357.srb-reminders-promo__dismiss-form {
     358        flex: 0 0 auto;
     359        margin: 0;
     360}
     361
     362.srb-reminders-promo__dismiss {
     363        color: #50575e;
     364        text-decoration: none;
     365}
     366
     367.srb-reminders-promo__dismiss:hover,
     368.srb-reminders-promo__dismiss:focus {
     369        color: #1d2327;
     370        text-decoration: underline;
     371}
     372
     373.srb-reminders-promo__dismiss:focus {
     374        box-shadow: 0 0 0 1px #2271b1;
     375        outline: 1px solid transparent;
     376}
     377
     378.srb-reminders-promo__body {
     379        margin-top: 10px;
     380}
     381
     382.srb-reminders-promo__actions {
     383        margin-top: 14px;
     384        margin-bottom: 0;
     385}
  • smsreminder-appointment-booking/trunk/assets/admin-booking-settings.js

    r3519533 r3548033  
    233233        }
    234234
     235
     236
     237        function trackEvent( eventName ) {
     238                if (
     239                        typeof srbAdminConfig === 'undefined' ||
     240                        ! srbAdminConfig.bearerToken ||
     241                        ! srbAdminConfig.apiBase ||
     242                        ! srbAdminConfig.analyticsTrackPath ||
     243                        ! eventName
     244                ) {
     245                        return;
     246                }
     247
     248                var base = String( srbAdminConfig.apiBase ).replace( /\/$/, '' );
     249                var trackPath = String( srbAdminConfig.analyticsTrackPath ).replace( /^\//, '' );
     250                var url = base + '/' + trackPath;
     251
     252                try {
     253                        fetch( url, {
     254                                method: 'POST',
     255                                headers: {
     256                                        'Content-Type': 'application/json',
     257                                        Authorization: 'Bearer ' + srbAdminConfig.bearerToken,
     258                                },
     259                                body: JSON.stringify( { event: eventName } ),
     260                                keepalive: true,
     261                        } ).catch( function () {
     262                                // Analytics failures are silent; admin UX must not be blocked.
     263                        } );
     264                } catch ( error ) {
     265                        // Analytics failures are silent; admin UX must not be blocked.
     266                }
     267        }
     268
     269        function initRemindersPromoAnalytics() {
     270                var promoBlock = document.querySelector( '[data-srb-reminders-promo]' );
     271                if ( promoBlock ) {
     272                        trackEvent( 'wordpress_settings_reminders_promo_block_viewed' );
     273
     274                        var enableLink = promoBlock.querySelector( '[data-srb-reminders-promo-enable]' );
     275                        if ( enableLink ) {
     276                                enableLink.addEventListener( 'click', function () {
     277                                        trackEvent( 'wordpress_settings_reminders_promo_block_enable_click' );
     278                                } );
     279                        }
     280
     281                        var learnMoreLink = promoBlock.querySelector( '[data-srb-reminders-promo-learn-more]' );
     282                        if ( learnMoreLink ) {
     283                                learnMoreLink.addEventListener( 'click', function () {
     284                                        trackEvent( 'wordpress_settings_reminders_promo_block_learn_more_click' );
     285                                } );
     286                        }
     287                }
     288
     289                var tooltipLearnMore = document.querySelector( '[data-srb-reminders-column-tooltip-learn-more]' );
     290                if ( tooltipLearnMore ) {
     291                        tooltipLearnMore.addEventListener( 'click', function () {
     292                                trackEvent( 'wordpress_settings_reminders_column_tooltip_learn_more_click' );
     293                        } );
     294                }
     295        }
     296
     297        function closeHelpTip( wrap ) {
     298                var button = wrap.querySelector( '.srb-help-tip' );
     299                var popover = wrap.querySelector( '.srb-help-tip-popover' );
     300                if ( ! button || ! popover ) {
     301                        return;
     302                }
     303
     304                popover.hidden = true;
     305                button.setAttribute( 'aria-expanded', 'false' );
     306                button.removeAttribute( 'aria-describedby' );
     307        }
     308
     309        function closeAllHelpTips( exceptWrap ) {
     310                var wraps = document.querySelectorAll( '.srb-help-tip-wrap' );
     311                wraps.forEach( function ( wrap ) {
     312                        if ( exceptWrap && wrap === exceptWrap ) {
     313                                return;
     314                        }
     315                        closeHelpTip( wrap );
     316                } );
     317        }
     318
     319        function toggleHelpTip( wrap ) {
     320                var button = wrap.querySelector( '.srb-help-tip' );
     321                var popover = wrap.querySelector( '.srb-help-tip-popover' );
     322                if ( ! button || ! popover ) {
     323                        return;
     324                }
     325
     326                if ( popover.hidden ) {
     327                        closeAllHelpTips( wrap );
     328                        popover.hidden = false;
     329                        button.setAttribute( 'aria-expanded', 'true' );
     330                        if ( popover.id ) {
     331                                button.setAttribute( 'aria-describedby', popover.id );
     332                        }
     333                        return;
     334                }
     335
     336                closeHelpTip( wrap );
     337        }
     338
     339        function initHelpTips() {
     340                var wraps = document.querySelectorAll( '.srb-help-tip-wrap' );
     341                if ( ! wraps.length ) {
     342                        return;
     343                }
     344
     345                wraps.forEach( function ( wrap ) {
     346                        var button = wrap.querySelector( '.srb-help-tip' );
     347                        if ( ! button ) {
     348                                return;
     349                        }
     350
     351                        button.addEventListener( 'click', function ( event ) {
     352                                event.preventDefault();
     353                                event.stopPropagation();
     354                                toggleHelpTip( wrap );
     355                        } );
     356                } );
     357
     358                document.addEventListener( 'click', function ( event ) {
     359                        var target = event.target;
     360                        if ( target instanceof Element && target.closest( '.srb-help-tip-wrap' ) ) {
     361                                return;
     362                        }
     363                        closeAllHelpTips();
     364                } );
     365
     366                document.addEventListener( 'keydown', function ( event ) {
     367                        if ( event.key === 'Escape' ) {
     368                                closeAllHelpTips();
     369                        }
     370                } );
     371        }
     372
    235373        function initCopyBookingLinkButton() {
    236374                var button = document.querySelector( '[data-srb-copy-booking-link]' );
    … …  
    263401        initColorValueSync();
    264402        initButtonPreview();
     403        initHelpTips();
    265404        initCopyBookingLinkButton();
     405        initRemindersPromoAnalytics();
    266406}() );
  • smsreminder-appointment-booking/trunk/includes/Admin/class-connection-actions.php

    r3519533 r3548033  
    99
    1010use SmsReminderBooking\Services\Connection_Service;
     11use SmsReminderBooking\Support\Reminders_Promo_Visibility;
    1112
    1213defined( 'ABSPATH' ) || exit;
    … …  
    2728                add_action( 'admin_post_sms_reminder_booking_connect', array( $this, 'handle_connect' ) );
    2829                add_action( 'admin_post_sms_reminder_booking_disconnect', array( $this, 'handle_disconnect' ) );
     30                add_action( 'admin_post_sms_reminder_booking_dismiss_promo', array( $this, 'handle_dismiss_promo' ) );
    2931                add_action( 'admin_post_sms_reminder_booking_callback', array( $this, 'handle_callback' ) );
    3032        }
    … …  
    7577        }
    7678
     79        public function handle_dismiss_promo(): void {
     80                if ( ! current_user_can( Reminders_Promo_Visibility::DISMISS_REQUIRED_CAPABILITY ) ) {
     81                        wp_die( esc_html__( 'You do not have permission to dismiss this notice.', 'smsreminder-appointment-booking' ), '', array( 'response' => 403 ) );
     82                }
     83                check_admin_referer( Reminders_Promo_Visibility::DISMISS_NONCE_ACTION );
     84
     85                $payload = Reminders_Promo_Visibility::dismiss_persist_payload( get_current_user_id() );
     86                if ( $payload !== null ) {
     87                        update_user_meta( $payload['user_id'], $payload['meta_key'], $payload['meta_value'] );
     88                        $this->service->track_analytics_event( $payload['analytics_event'] );
     89                }
     90
     91                wp_safe_redirect( self::settings_url_with_query( array() ) );
     92                exit;
     93        }
     94
    7795        public function handle_callback(): void {
    7896                if ( ! current_user_can( 'manage_options' ) ) {
  • smsreminder-appointment-booking/trunk/includes/Admin/class-settings-page.php

    r3519533 r3548033  
    1010use SmsReminderBooking\Services\Connection_Service;
    1111use SmsReminderBooking\Support\Book_Button_Defaults;
     12use SmsReminderBooking\Support\Reminders_Promo_Resolution;
     13use SmsReminderBooking\Support\Reminders_Promo_Visibility;
    1214
    1315defined( 'ABSPATH' ) || exit;
    1416
    1517require_once SMS_REMINDER_BOOKING_PLUGIN_DIR . 'includes/Support/class-book-button-defaults.php';
     18require_once SMS_REMINDER_BOOKING_PLUGIN_DIR . 'includes/Support/class-reminders-promo-visibility.php';
     19require_once SMS_REMINDER_BOOKING_PLUGIN_DIR . 'includes/Support/class-reminders-promo-resolution.php';
    1620
    1721final class Settings_Page {
    … …  
    2226        private Connection_Service $service;
    2327
     28        private bool $has_paid_subscription = false;
     29
     30        private bool $promo_visible = false;
     31
    2432        public function __construct( Connection_Service $service ) {
    2533                $this->service = $service;
     34        }
     35
     36        public function get_has_paid_subscription(): bool {
     37                return $this->has_paid_subscription;
     38        }
     39
     40        public function get_should_show_reminders_promo(): bool {
     41                return $this->promo_visible;
    2642        }
    2743
    … …  
    4763                                        <?php
    4864                                else :
     65                                        $profile = Reminders_Promo_Visibility::needs_profile_for_promo(
     66                                                $booking_settings,
     67                                                $this->is_promo_dismissed()
     68                                        )
     69                                                ? $this->service->fetch_profile()
     70                                                : null;
     71
     72                                        if ( is_wp_error( $profile ) ) {
     73                                                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     74                                                error_log(
     75                                                        sprintf(
     76                                                                'SMS Reminder Booking: profile fetch failed (%s); hiding reminders promo.',
     77                                                                $profile->get_error_message()
     78                                                        )
     79                                                );
     80                                        }
     81
     82                                        $resolution = Reminders_Promo_Resolution::resolve(
     83                                                $booking_settings,
     84                                                $profile ?? array(),
     85                                                $this->is_promo_dismissed()
     86                                        );
     87                                        $this->promo_visible         = $resolution['promo_visible'];
     88                                        $this->has_paid_subscription = $resolution['has_paid_subscription'];
     89
    4990                                        $this->render_booking_sections( $booking_settings );
     91                                        if ( $this->promo_visible ) {
     92                                                $this->render_reminders_promo_block();
     93                                        }
    5094                                endif;
    5195                                ?>
    … …  
    60104
    61105                                <div class="srb-card">
    62                                         <p><?php esc_html_e( 'Connect your WordPress site to SMS Reminder to enable booking features.', 'smsreminder-appointment-booking' ); ?></p>
     106                                        <h2><?php esc_html_e( 'Enable bookings and reduce no-shows', 'smsreminder-appointment-booking' ); ?></h2>
     107                                        <p><?php esc_html_e( 'Connect your WordPress site to SMS Reminder to enable instant client self-booking.', 'smsreminder-appointment-booking' ); ?></p>
    63108
    64109                                        <?php $this->render_connect_external_services_disclosure(); ?>
    … …  
    75120                                                </a>
    76121                                        </p>
     122                                        <p class="description"><?php esc_html_e( 'Send automated SMS reminders to clients (optional).', 'smsreminder-appointment-booking' ); ?></p>
    77123                                </div>
    78124                        <?php endif; ?>
    … …  
    99145                $dashboard_url = $this->service->get_plugin_config()->get_frontend_dashboard_settings_url();
    100146                $setup_booking_page_url = rtrim( $this->service->get_plugin_config()->get_frontend_site_url(), '/' ) . '/dashboard/settings?tab=6';
    101                 $onboarding_completed = $this->booking_onboarding_completed( $booking_settings );
     147                $onboarding_completed = Reminders_Promo_Visibility::booking_onboarding_completed( $booking_settings );
    102148                if ( ! $onboarding_completed ) {
    103149                        $this->render_finish_setup_section( $setup_booking_page_url );
    … …  
    189235                                        <th scope="col"><?php esc_html_e( 'Buffer after', 'smsreminder-appointment-booking' ); ?></th>
    190236                                        <th scope="col"><?php esc_html_e( 'Minimum notice', 'smsreminder-appointment-booking' ); ?></th>
    191                                         <th scope="col"><?php esc_html_e( 'Reminders', 'smsreminder-appointment-booking' ); ?></th>
     237                                        <th scope="col"><?php $this->render_reminders_column_header(); ?></th>
    192238                                        <th scope="col"><?php esc_html_e( 'Rescheduling', 'smsreminder-appointment-booking' ); ?></th>
    193239                                </tr>
    … …  
    284330                        </tbody>
    285331                        </table>
     332                </div>
     333                <?php
     334        }
     335
     336        private function render_reminders_promo_block(): void {
     337                $enable_url     = $this->service->get_plugin_config()->get_enable_reminders_url();
     338                $learn_more_url = $this->service->get_plugin_config()->get_marketing_learn_more_url();
     339                ?>
     340                <div class="srb-card srb-reminders-promo" data-srb-reminders-promo>
     341                        <div class="srb-reminders-promo__header">
     342                                <h2><?php esc_html_e( 'Reduce no-shows with SMS reminders', 'smsreminder-appointment-booking' ); ?></h2>
     343                                <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" class="srb-reminders-promo__dismiss-form">
     344                                        <input type="hidden" name="action" value="sms_reminder_booking_dismiss_promo" />
     345                                        <?php wp_nonce_field( Reminders_Promo_Visibility::DISMISS_NONCE_ACTION ); ?>
     346                                        <button
     347                                                type="submit"
     348                                                class="button-link srb-reminders-promo__dismiss"
     349                                                data-srb-reminders-promo-dismiss
     350                                        >
     351                                                <?php esc_html_e( 'Dismiss', 'smsreminder-appointment-booking' ); ?>
     352                                        </button>
     353                                </form>
     354                        </div>
     355                        <p class="srb-reminders-promo__body">
     356                                <?php esc_html_e( 'Send automatic reminders before appointments so clients actually show up.', 'smsreminder-appointment-booking' ); ?>
     357                        </p>
     358                        <p class="srb-reminders-promo__actions srb-inline-actions">
     359                                <a
     360                                        class="button button-primary"
     361                                        href="<?php echo esc_url( $enable_url ); ?>"
     362                                        target="_blank"
     363                                        rel="noopener noreferrer"
     364                                        data-srb-reminders-promo-enable
     365                                >
     366                                        <?php esc_html_e( 'Enable SMS reminders', 'smsreminder-appointment-booking' ); ?>
     367                                </a>
     368                                <a
     369                                        href="<?php echo esc_url( $learn_more_url ); ?>"
     370                                        target="_blank"
     371                                        rel="noopener noreferrer"
     372                                        data-srb-reminders-promo-learn-more
     373                                >
     374                                        <?php esc_html_e( 'Learn more', 'smsreminder-appointment-booking' ); ?>
     375                                </a>
     376                        </p>
    286377                </div>
    287378                <?php
    … …  
    663754
    664755        /**
    665          * @param array{booking_onboarding:array<string,mixed>|null} $booking_settings
    666          */
    667         private function booking_onboarding_completed( array $booking_settings ): bool {
    668                 $onboarding = $this->get_value( $booking_settings, 'booking_onboarding' );
    669                 if ( ! is_array( $onboarding ) ) {
     756         * @param array|\WP_Error $profile
     757         */
     758        private function is_promo_dismissed(): bool {
     759                $user_id = get_current_user_id();
     760                if ( $user_id <= 0 ) {
    670761                        return false;
    671762                }
    672763
    673                 return true === $this->get_value( $onboarding, 'onboarding_completed' );
     764                return get_user_meta( $user_id, Reminders_Promo_Visibility::DISMISS_USER_META_KEY, true ) === '1';
    674765        }
    675766
    … …  
    739830        }
    740831
     832        private function render_reminders_column_header(): void {
     833                $learn_more_url = $this->service->get_plugin_config()->get_marketing_learn_more_url();
     834                ?>
     835                <span class="srb-reminders-column-header">
     836                        <?php esc_html_e( 'Reminders', 'smsreminder-appointment-booking' ); ?>
     837                        <span class="srb-help-tip-wrap">
     838                                <button
     839                                        type="button"
     840                                        class="srb-help-tip dashicons dashicons-info"
     841                                        aria-expanded="false"
     842                                        aria-controls="srb-reminders-column-tooltip"
     843                                >
     844                                        <span class="screen-reader-text">
     845                                                <?php esc_html_e( 'More information about optional SMS reminders', 'smsreminder-appointment-booking' ); ?>
     846                                        </span>
     847                                </button>
     848                                <div
     849                                        id="srb-reminders-column-tooltip"
     850                                        class="srb-help-tip-popover"
     851                                        role="tooltip"
     852                                        hidden
     853                                >
     854                                        <p class="srb-help-tip-popover__title">
     855                                                <strong><?php esc_html_e( 'Optional SMS reminders', 'smsreminder-appointment-booking' ); ?></strong>
     856                                        </p>
     857                                        <p class="srb-help-tip-popover__body">
     858                                                <?php esc_html_e( 'Automatically send appointment reminders to clients by SMS before their booking.', 'smsreminder-appointment-booking' ); ?>
     859                                        </p>
     860                                        <p class="srb-help-tip-popover__action">
     861                                                <a
     862                                                        href="<?php echo esc_url( $learn_more_url ); ?>"
     863                                                        target="_blank"
     864                                                        rel="noopener noreferrer"
     865                                                        data-srb-reminders-column-tooltip-learn-more
     866                                                >
     867                                                        <?php esc_html_e( 'Learn more', 'smsreminder-appointment-booking' ); ?>
     868                                                </a>
     869                                        </p>
     870                                </div>
     871                        </span>
     872                </span>
     873                <?php
     874        }
     875
    741876        private static function format_connected_at( string $iso ): string {
    742877                if ( $iso === '' ) {
  • smsreminder-appointment-booking/trunk/includes/Services/class-connection-service.php

    r3522716 r3548033  
    349349
    350350                $result = $this->client->get_json( 'connected-apps/booking/settings', (string) $conn['token'] );
     351                return $this->parse_booking_settings_result( $result );
     352        }
     353
     354        /**
     355         * Fetches connected user profile (subscription signals for promo visibility).
     356         *
     357         * @return array{user:array{id:string,email:string,billing_subscription_id:int|null,has_paid_subscription:bool,has_reminder_setup_history:bool}}|\WP_Error
     358         */
     359        public function fetch_profile() {
     360                $conn = $this->store->get_connection();
     361                if ( $conn === null || empty( $conn['token'] ) ) {
     362                        return new \WP_Error( 'srb_no_token', __( 'There is no saved connection token.', 'smsreminder-appointment-booking' ) );
     363                }
     364
     365                $result = $this->client->get_json( 'connected-apps/profile', (string) $conn['token'] );
     366                return $this->parse_profile_result( $result );
     367        }
     368
     369        public function track_analytics_event( string $event ): void {
     370                $conn = $this->store->get_connection();
     371                if ( $conn === null || empty( $conn['token'] ) || $event === '' ) {
     372                        return;
     373                }
     374
     375                $result = $this->client->post_json(
     376                        'connected-apps/analytics/track',
     377                        array( 'event' => $event ),
     378                        (string) $conn['token']
     379                );
     380
     381                if ( ! $result['ok'] ) {
     382                        // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     383                        error_log(
     384                                sprintf(
     385                                        'SMS Reminder Booking: analytics track failed for %s (%s).',
     386                                        $event,
     387                                        $result['error_message'] ?? 'unknown error'
     388                                )
     389                        );
     390                }
     391        }
     392
     393        /**
     394         * @param array{ok:bool, status:int, data?:mixed, error_message?:string} $result
     395         * @return array{booking_url:string, booking_onboarding:array<string,mixed>|null, availability:array<string,mixed>, appointment_types:array<int,mixed>, upcoming_bookings:array<int,mixed>}|\WP_Error
     396         */
     397        private function parse_booking_settings_result( array $result ) {
    351398                if ( ! $result['ok'] ) {
    352399                        return new \WP_Error(
    … …  
    371418
    372419                return array(
    373                         'booking_url'       => isset( $data['booking_url'] ) ? (string) $data['booking_url'] : '',
     420                        'booking_url'        => isset( $data['booking_url'] ) ? (string) $data['booking_url'] : '',
    374421                        'booking_onboarding' => ( isset( $data['booking_onboarding'] ) && is_array( $data['booking_onboarding'] ) ) ? $data['booking_onboarding'] : null,
    375                         'availability'      => $data['availability'],
    376                         'appointment_types' => $data['appointment_types'],
    377                         'upcoming_bookings' => $data['upcoming_bookings'],
     422                        'availability'       => $data['availability'],
     423                        'appointment_types'    => $data['appointment_types'],
     424                        'upcoming_bookings'  => $data['upcoming_bookings'],
     425                );
     426        }
     427
     428        /**
     429         * @param array{ok:bool, status:int, data?:mixed, error_message?:string} $result
     430         * @return array{user:array{id:string,email:string,billing_subscription_id:int|null,has_paid_subscription:bool,has_reminder_setup_history:bool}}|\WP_Error
     431         */
     432        private function parse_profile_result( array $result ) {
     433                if ( ! $result['ok'] ) {
     434                        return new \WP_Error(
     435                                'srb_profile',
     436                                $result['error_message'] ?? __( 'Could not load profile from SMS Reminder.', 'smsreminder-appointment-booking' )
     437                        );
     438                }
     439
     440                $data = $result['data'] ?? null;
     441                if ( ! is_array( $data ) || ! isset( $data['user'] ) || ! is_array( $data['user'] ) ) {
     442                        return new \WP_Error( 'srb_profile_shape', __( 'Unexpected profile response.', 'smsreminder-appointment-booking' ) );
     443                }
     444
     445                $user = $data['user'];
     446
     447                return array(
     448                        'user' => array(
     449                                'id'                      => isset( $user['id'] ) ? (string) $user['id'] : '',
     450                                'email'                   => isset( $user['email'] ) ? (string) $user['email'] : '',
     451                                'billing_subscription_id' => isset( $user['billing_subscription_id'] ) && is_numeric( $user['billing_subscription_id'] )
     452                                        ? (int) $user['billing_subscription_id']
     453                                        : null,
     454                                'has_paid_subscription'   => true === ( $user['has_paid_subscription'] ?? false ),
     455                                'has_reminder_setup_history' => true === ( $user['has_reminder_setup_history'] ?? false ),
     456                        ),
    378457                );
    379458        }
  • smsreminder-appointment-booking/trunk/includes/class-admin.php

    r3519533 r3548033  
    9191                        'smsreminder-appointment-booking-admin-settings',
    9292                        SMS_REMINDER_BOOKING_PLUGIN_URL . 'assets/admin-booking-settings.css',
    93                         array(),
     93                        array( 'dashicons' ),
    9494                        $style_ver
     95                );
     96
     97                if ( ! current_user_can( 'manage_options' ) ) {
     98                        return;
     99                }
     100
     101                $connection  = $this->connection_service->get_store()->get_connection();
     102                $bearer_token = is_array( $connection ) && ! empty( $connection['token'] )
     103                        ? (string) $connection['token']
     104                        : '';
     105
     106                wp_localize_script(
     107                        'smsreminder-appointment-booking-admin-settings',
     108                        'srbAdminConfig',
     109                        array(
     110                                'apiBase'            => $this->plugin_config->get_api_base(),
     111                                'analyticsTrackPath' => 'connected-apps/analytics/track',
     112                                'bearerToken'        => $bearer_token,
     113                        )
    95114                );
    96115        }
  • smsreminder-appointment-booking/trunk/includes/class-plugin-config.php

    r3519533 r3548033  
    9393        }
    9494
     95        public function get_enable_reminders_url(): string {
     96                return add_query_arg(
     97                        array(
     98                                'source'       => 'wordpress',
     99                                'utm_source'   => 'wordpress-plugin',
     100                                'utm_medium'   => 'admin',
     101                                'utm_campaign' => 'enable_reminders',
     102                        ),
     103                        $this->get_frontend_dashboard_settings_url()
     104                );
     105        }
     106
     107        public function get_marketing_learn_more_url(): string {
     108                return add_query_arg(
     109                        array(
     110                                'utm_source'   => 'wordpress-plugin',
     111                                'utm_medium'   => 'admin',
     112                                'utm_campaign' => 'learn_more',
     113                        ),
     114                        'https://www.smsreminder.app/'
     115                );
     116        }
     117
    95118        public function get_http_timeout(): int {
    96119                if ( $this->http_timeout_cache !== null ) {
  • smsreminder-appointment-booking/trunk/readme.txt

    r3522716 r3548033  
    55Tested up to: 6.9
    66Requires PHP: 8.2
    7 Stable tag: 1.0.1
     7Stable tag: 1.1.0
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    137137== Changelog ==
    138138
     139= 1.1.0 =
     140* Connection screen: updated copy to highlight free booking and optional SMS reminders before sign-up.
     141* Connected dashboard: added a dismissible SMS reminders promo block with a direct link to enable reminders in the SMS Reminder dashboard.
     142* Appointment types table: added an informational tooltip on the Reminders column explaining optional SMS reminders.
     143* Admin analytics: track promo block and tooltip impressions and CTA clicks for product funnel measurement.
     144
    139145= 1.0.1 =
    140146* Connection flow: the redirect to the SMS Reminder web app now includes a `source` query parameter (`wordpress`) so new signups started from this plugin can be attributed correctly in product analytics. No change to stored tokens, callbacks, or booking behavior.
    … …  
    148154== Upgrade Notice ==
    149155
     156= 1.1.0 =
     157Marketing and education updates for optional SMS reminders. No settings changes or reconnection required.
     158
    150159= 1.0.1 =
    151160Maintenance release. Improves how the SMS Reminder service attributes new signups that start from this plugin. No settings changes or reconnection required.
  • smsreminder-appointment-booking/trunk/smsreminder-appointment-booking.php

    r3522716 r3548033  
    44 * Plugin URI:        https://www.smsreminder.app/
    55 * Description:       Let clients book appointments through Google Calendar and reduce no-shows with simple scheduling and SMS reminders.
    6  * Version:           1.0.1
     6 * Version:           1.1.0
    77 * Requires at least: 6.9
    88 * Requires PHP:      8.2
    … …  
    1717defined( 'ABSPATH' ) || exit;
    1818
    19 define( 'SMS_REMINDER_BOOKING_VERSION', '1.0.1' );
     19define( 'SMS_REMINDER_BOOKING_VERSION', '1.1.0' );
    2020/** Increment when the custom DB schema changes (tables/columns); not the same as plugin semver. */
    2121define( 'SMS_REMINDER_BOOKING_DB_VERSION', 1 );
  • smsreminder-appointment-booking/trunk/uninstall.php

    r3519533 r3548033  
    88defined( 'WP_UNINSTALL_PLUGIN' ) || exit;
    99
     10require_once plugin_dir_path( __FILE__ ) . 'includes/Support/class-reminders-promo-visibility.php';
     11
     12use SmsReminderBooking\Support\Reminders_Promo_Visibility;
     13
    1014delete_option( 'sms_reminder_booking_button_defaults' );
    1115delete_option( 'sms_reminder_booking_connection' );
    … …  
    1317delete_transient( 'sms_reminder_booking_pending_auth' );
    1418foreach ( get_users( array( 'fields' => 'ID' ) ) as $sms_reminder_booking_user_id ) {
    15         delete_transient( 'sms_reminder_booking_pending_auth_' . (int) $sms_reminder_booking_user_id );
     19        $user_id = (int) $sms_reminder_booking_user_id;
     20        delete_transient( 'sms_reminder_booking_pending_auth_' . $user_id );
     21        delete_user_meta( $user_id, Reminders_Promo_Visibility::DISMISS_USER_META_KEY );
    1622}
    1723delete_option( 'sms_reminder_booking_db_version' );
Note: See TracChangeset for help on using the changeset viewer.