Plugin Directory

Changeset 3468659


Ignore:
Timestamp:
02/24/2026 01:12:12 PM (5 weeks ago)
Author:
creavi
Message:

Added Service setup onboarding flow

Location:
creavi-booking-service
Files:
53 added
9 edited

Legend:

Unmodified
Added
Removed
  • creavi-booking-service/trunk/assets/css/admin.css

    r3456986 r3468659  
    769769  margin-bottom: 6px;  /* space between label and control */
    770770  font-weight: 600;
     771  font-size: 1.1em !important;
    771772}
    772773
     
    909910  }
    910911}
     912
     913/* =========================================
     914   Service Basics boxes — match Creavi metabox UI
     915   Uses the same gradient border + glass layers
     916========================================= */
     917
     918#creavibc_service_title_box.postbox,
     919#creavibc_service_desc_box.postbox{
     920  position: relative;
     921  border: 1px solid transparent !important;
     922  border-radius: 18px !important;
     923  background: transparent !important;
     924  overflow: hidden;
     925  box-shadow: var(--creavibc-shadow-soft) !important;
     926  margin-bottom: 18px;
     927}
     928
     929/* thin GRADIENT BORDER (1px) */
     930#creavibc_service_title_box.postbox::before,
     931#creavibc_service_desc_box.postbox::before{
     932  content:"";
     933  position:absolute;
     934  inset:0;
     935  padding: 1px;
     936  border-radius: 18px;
     937  background: linear-gradient(135deg,
     938      rgba(120,2,159,.55),
     939      rgba(221,129,249,.45),
     940      rgba(17,24,39,.08)
     941  );
     942  -webkit-mask:
     943    linear-gradient(#000 0 0) content-box,
     944    linear-gradient(#000 0 0);
     945  -webkit-mask-composite: xor;
     946          mask-composite: exclude;
     947  pointer-events:none;
     948}
     949
     950/* glass background layer inside */
     951#creavibc_service_title_box.postbox::after,
     952#creavibc_service_desc_box.postbox::after{
     953  content:"";
     954  position:absolute;
     955  inset:1px;
     956  border-radius: 17px;
     957  background:
     958    radial-gradient(1200px 220px at 20% 0%,
     959      rgba(221,129,249,.16),
     960      rgba(255,255,255,0) 60%),
     961    radial-gradient(1000px 240px at 90% 20%,
     962      rgba(120,2,159,.10),
     963      rgba(255,255,255,0) 55%),
     964    linear-gradient(180deg,
     965      var(--creavibc-glass) 0%,
     966      var(--creavibc-glass-2) 100%);
     967  backdrop-filter: blur(10px);
     968  -webkit-backdrop-filter: blur(10px);
     969  pointer-events:none;
     970}
     971
     972/* ensure header/content above layers */
     973#creavibc_service_title_box.postbox > *,
     974#creavibc_service_desc_box.postbox > *{
     975  position: relative;
     976  z-index: 1;
     977}
     978
     979/* header = same as your other metaboxes */
     980#creavibc_service_title_box .postbox-header,
     981#creavibc_service_desc_box .postbox-header{
     982  position: relative;
     983  border-bottom: 1px solid rgba(17,24,39,.08) !important;
     984  background: transparent !important;
     985  padding: 14px 5px 12px !important;
     986}
     987
     988/* accent hairline */
     989#creavibc_service_title_box .postbox-header::before,
     990#creavibc_service_desc_box .postbox-header::before{
     991  content:"";
     992  position:absolute;
     993  left: 16px;
     994  top: 10px;
     995  width: 70px;
     996  height: 3px;
     997  border-radius: 999px;
     998  background: linear-gradient(90deg, var(--creavibc-accent), var(--creavibc-accent-2));
     999  opacity: .85;
     1000}
     1001
     1002#creavibc_service_title_box .postbox-header h2,
     1003#creavibc_service_desc_box .postbox-header h2{
     1004  font-weight: 800 !important;
     1005  letter-spacing: -0.02em;
     1006  color: var(--creavibc-text) !important;
     1007}
     1008
     1009/* inside padding consistent */
     1010#creavibc_service_title_box .inside,
     1011#creavibc_service_desc_box .inside{
     1012  padding: 16px !important;
     1013}
     1014
     1015/* Title input: same Apple-ish fields */
     1016.post-type-creavibc_service #title{
     1017  border-radius: 14px !important;
     1018  border: 1px solid rgba(17,24,39,.10) !important;
     1019  background: rgba(255,255,255,.78) !important;
     1020  box-shadow:
     1021    0 1px 0 rgba(255,255,255,.35) inset,
     1022    0 10px 24px rgba(0,0,0,.05);
     1023  padding: 12px 14px !important;
     1024  font-size: 18px !important;
     1025}
     1026.post-type-creavibc_service #title:focus{
     1027  outline: none !important;
     1028  border-color: rgba(120,2,159,.38) !important;
     1029  box-shadow:
     1030    0 0 0 3px rgba(255,255,255,.9),
     1031    0 0 0 7px var(--creavibc-ring-2),
     1032    0 12px 28px rgba(0,0,0,.08) !important;
     1033}
     1034
     1035/* Editor: make it feel like it belongs inside the box */
     1036.post-type-creavibc_service #postdivrich{
     1037  background: transparent !important;
     1038  border: 0 !important;
     1039  box-shadow: none !important;
     1040  margin: 0 !important;
     1041}
     1042
     1043/* Smaller editor (your request) */
     1044.post-type-creavibc_service #content_ifr{ height: 140px !important; }
     1045.post-type-creavibc_service #content{ min-height: 140px !important; }
     1046
     1047/* Remove extra top spacing WP adds around title section */
     1048.post-type-creavibc_service #titlediv{ margin: 0 !important; }
     1049.post-type-creavibc_service #titlewrap{ margin: 0 !important; }
     1050
     1051/* Creavi Booking Service — Shortcode metabox (admin)
     1052   File: assets/css/admin-shortcode-metabox.css */
     1053
     1054#creavibc_shortcode_box .inside{
     1055  padding-top: 10px;
     1056}
     1057
     1058#creavibc_shortcode_box .creavibc-sc-title{
     1059  margin: 0 0 6px;
     1060  font-weight: 700;
     1061  font-size: 13px;
     1062  color: rgba(0,0,0,.90);
     1063}
     1064
     1065#creavibc_shortcode_box .creavibc-sc-desc{
     1066  margin-top: 0;
     1067  margin-bottom: 10px;
     1068}
     1069
     1070#creavibc_shortcode_box .creavibc-sc-fieldwrap{
     1071  margin-bottom: 10px;
     1072}
     1073
     1074#creavibc_shortcode_box .creavibc-sc-input{
     1075  width: 100%;
     1076  height: 44px;
     1077  padding: 10px 12px;
     1078
     1079  border-radius: 12px;
     1080  border: 1px solid rgba(120,2,159,.22);
     1081
     1082  background: linear-gradient(180deg, rgba(221,129,249,.12), rgba(255,255,255,.92));
     1083  box-shadow: 0 10px 24px rgba(0,0,0,.06);
     1084
     1085  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
     1086  font-size: 13px;
     1087  color: rgba(0,0,0,.90);
     1088
     1089  outline: none;
     1090}
     1091
     1092#creavibc_shortcode_box .creavibc-sc-input:focus{
     1093  border-color: rgba(120,2,159,.38);
     1094  box-shadow: 0 0 0 3px rgba(120,2,159,.14), 0 10px 24px rgba(0,0,0,.06);
     1095}
     1096
     1097#creavibc_shortcode_box .creavibc-sc-actions{
     1098  display: flex;
     1099  justify-content: flex-end;
     1100}
     1101
     1102#creavibc_shortcode_box .creavibc-sc-copy{
     1103  border-radius: 12px;
     1104  border: 1px solid rgba(120,2,159,.22);
     1105  background: #fff;
     1106  padding: 6px 12px;
     1107  min-height: 36px;
     1108
     1109  display: inline-flex;
     1110  align-items: center;
     1111  gap: 8px;
     1112
     1113  transition: transform .3s ease, border-color .3s ease, box-shadow .3s ease;
     1114}
     1115
     1116#creavibc_shortcode_box .creavibc-sc-copy:hover{
     1117  border-color: rgba(120,2,159,.45);
     1118  box-shadow: 0 10px 24px rgba(120,2,159,.10);
     1119  transform: translateY(-1px);
     1120}
     1121
     1122#creavibc_shortcode_box .creavibc-sc-copy:active{
     1123  transform: translateY(0);
     1124}
     1125
     1126#creavibc_shortcode_box .creavibc-sc-copy.is-done .creavibc-sc-copy-default{ display:none; }
     1127#creavibc_shortcode_box .creavibc-sc-copy.is-done .creavibc-sc-copy-done{ display:inline; }
     1128#creavibc_shortcode_box .creavibc-sc-copy-done{ display:none; }
     1129
  • creavi-booking-service/trunk/assets/js/admin.js

    r3454590 r3468659  
    673673});
    674674
     675document.addEventListener('DOMContentLoaded', function () {
     676
     677  // Shortcode metabox id is "creavibc_shortcode_box"
     678  const box = document.getElementById('creavibc_shortcode_box');
     679  if (!box) return;
     680
     681  const input = box.querySelector('.creavibc-sc-input');
     682  const copyBtn = box.querySelector('.creavibc-sc-copy');
     683  if (!input || !copyBtn) return;
     684
     685  // Click/focus selects the shortcode (nice UX)
     686  function selectField() {
     687    try { input.focus(); input.select(); } catch (e) {}
     688  }
     689  input.addEventListener('click', selectField);
     690  input.addEventListener('focus', selectField);
     691
     692  // Copy (clipboard + fallback)
     693  function copyText(text) {
     694    if (navigator.clipboard && navigator.clipboard.writeText) {
     695      return navigator.clipboard.writeText(text);
     696    }
     697    return new Promise(function (resolve, reject) {
     698      try {
     699        const ta = document.createElement('textarea');
     700        ta.value = text;
     701        ta.setAttribute('readonly', '');
     702        ta.style.position = 'absolute';
     703        ta.style.left = '-9999px';
     704        document.body.appendChild(ta);
     705        ta.select();
     706        const ok = document.execCommand('copy');
     707        document.body.removeChild(ta);
     708        ok ? resolve() : reject();
     709      } catch (err) {
     710        reject(err);
     711      }
     712    });
     713  }
     714
     715  copyBtn.addEventListener('click', function () {
     716    const val = input.value || '';
     717    if (!val) return;
     718
     719    copyText(val).then(function () {
     720      copyBtn.classList.add('is-done');
     721      window.setTimeout(function () {
     722        copyBtn.classList.remove('is-done');
     723      }, 1200);
     724    }).catch(function () {
     725      // If copy fails, still select for manual copy
     726      selectField();
     727    });
     728  });
     729
     730});
     731
  • creavi-booking-service/trunk/creavi-booking-service.php

    r3460766 r3468659  
    55 * Text Domain: creavi-booking-service
    66 * Domain Path: /languages
    7  * Version: 1.1.9
     7 * Version: 1.2.0
    88 * Author: Creavi
    99 * License: GPL2
     
    1616define('CREAVIBC_PLUGIN_URL', plugin_dir_url(__FILE__));
    1717define('CREAVIBC_PLUGIN_PATH', plugin_dir_path(__FILE__));
    18 define('CREAVIBC_VERSION', '1.1.9');
     18define('CREAVIBC_VERSION', '1.2.0');
    1919
    2020
     
    3939
    4040
     41require_once CREAVIBC_PLUGIN_DIR . 'includes/activation-redirect.php';
     42require_once CREAVIBC_PLUGIN_PATH . 'includes/activation-metrics.php';
    4143require_once CREAVIBC_PLUGIN_DIR . 'includes/deactivation-feedback.php';
    42 
    4344require_once CREAVIBC_PLUGIN_DIR . 'includes/cbs-gcal-remote.php';
    44 
    4545require_once CREAVIBC_PLUGIN_DIR . 'includes/post-types.php';
    4646require_once CREAVIBC_PLUGIN_DIR . 'includes/save-service.php';
     
    5555
    5656register_activation_hook( CREAVIBC_PLUGIN_FILE, 'creavibc_setup_placeholder_attachment' );
     57register_activation_hook( CREAVIBC_PLUGIN_FILE, 'creavibc_metrics_mark_activated' );
    5758
    5859
  • creavi-booking-service/trunk/includes/admin.php

    r3439801 r3468659  
    22if ( ! defined( 'ABSPATH' ) ) exit;
    33
    4 add_action('admin_enqueue_scripts', function ($hook) {
    5     global $post;
    6 
    7     if ($hook === 'post-new.php' || $hook === 'post.php') {
    8         if ($post && $post->post_type === 'creavibc_service') {
    9 
    10             // Load local Flatpickr CSS & JS from plugin assets
    11             wp_enqueue_style(
    12                 'flatpickr',
    13                 CREAVIBC_PLUGIN_URL . 'assets/vendor/flatpickr/flatpickr.min.css',
    14                 [],
    15                 '4.6.13' // or whatever version you're using
    16             );
    17 
    18             wp_enqueue_script(
    19                 'flatpickr',
    20                 CREAVIBC_PLUGIN_URL . 'assets/vendor/flatpickr/flatpickr.min.js',
    21                 [],
    22                 '4.6.13',
    23                 true
    24             );
    25 
    26             // Local admin script
    27             wp_enqueue_script(
    28                 'creavibc-admin-js',
    29                 CREAVIBC_PLUGIN_URL . 'assets/js/admin.js',
    30                 ['flatpickr'],
    31                 filemtime(CREAVIBC_PLUGIN_PATH . 'assets/js/admin.js'),
    32                 true
    33             );
    34 
    35             wp_enqueue_style(
    36                 'creavibc-admin-style',
    37                 CREAVIBC_PLUGIN_URL . 'assets/css/admin.css',
    38                 [],
    39                 filemtime(CREAVIBC_PLUGIN_PATH . 'assets/css/admin.css')
    40             );
    41 
    42             wp_enqueue_script(
    43                 'creavibc-gcal-busy-admin',
    44                 CREAVIBC_PLUGIN_URL . 'assets/js/cbs-gcal-busy-admin.js',
    45                 [ 'jquery' ],
    46                 filemtime( CREAVIBC_PLUGIN_PATH . 'assets/js/cbs-gcal-busy-admin.js' ),
    47                 true
    48             );
    49 
    50             wp_localize_script(
    51                 'creavibc-gcal-busy-admin',
    52                 'creavibcGcalBusy',
    53                 [
    54                     'ajaxUrl'   => admin_url( 'admin-ajax.php' ),
    55                     'nonce'     => wp_create_nonce( 'creavibc_admin_nonce' ),
    56                     'serviceId' => $post->ID, // handy, optional
    57                 ]
    58             );           
    59 
    60 
    61         }
     4/**
     5 * Title placeholder: "Service name"
     6 */
     7add_filter( 'enter_title_here', function( $title, $post ) {
     8    if ( $post && $post->post_type === 'creavibc_service' ) {
     9        return __( 'Service name', 'creavi-booking-service' );
     10    }
     11    return $title;
     12}, 10, 2 );
     13
     14
     15/**
     16 * Wrap Title + Editor into metabox-like postboxes
     17 * (We MOVE #titlediv and #postdivrich into hosts via JS)
     18 */
     19add_action( 'edit_form_after_title', function( $post ) {
     20    if ( ! $post || $post->post_type !== 'creavibc_service' ) return;
     21    ?>
     22    <div id="creavibc-basics-wrap">
     23
     24        <!-- Service Title (metabox style) -->
     25        <div id="creavibc_service_title_box" class="postbox creavibc-basics-box">
     26            <div class="postbox-header">
     27                <h2 class="hndle ui-sortable-handle">
     28                    <span><?php esc_html_e( 'Service title', 'creavi-booking-service' ); ?></span>
     29                </h2>
     30            </div>
     31            <div class="inside">
     32                <p class="description" style="margin-top:0;">
     33                    <?php esc_html_e( 'This is shown to customers as the service name.', 'creavi-booking-service' ); ?>
     34                </p>
     35
     36                <!-- Move WP title field (#titlediv) here -->
     37                <div id="creavibc-title-host"></div>
     38            </div>
     39        </div>
     40
     41        <!-- Service Description (metabox style) -->
     42        <div id="creavibc_service_desc_box" class="postbox creavibc-basics-box">
     43            <div class="postbox-header">
     44                <h2 class="hndle ui-sortable-handle">
     45                    <span><?php esc_html_e( 'Service description', 'creavi-booking-service' ); ?></span>
     46                </h2>
     47            </div>
     48            <div class="inside">
     49                <p class="description" style="margin-top:0;">
     50                    <?php esc_html_e( 'Shown on the left side of the booking popup / inline block.', 'creavi-booking-service' ); ?>
     51                </p>
     52
     53                <!-- Move WP editor (#postdivrich) here -->
     54                <div id="creavibc-editor-host"></div>
     55            </div>
     56        </div>
     57
     58    </div>
     59    <?php
     60} );
     61
     62
     63/**
     64 * Make editor smaller + hide other notices only on Service edit screen
     65 */
     66add_action( 'admin_head', function() {
     67    $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
     68    if ( ! $screen || $screen->post_type !== 'creavibc_service' ) return;
     69
     70    // allow debugging: show notices if you add ?creavibc_show_notices=1
     71    $show_notices = ( isset( $_GET['creavibc_show_notices'] ) && $_GET['creavibc_show_notices'] === '1' );
     72
     73    echo '<style>
     74        /* Smaller editor */
     75        .post-type-creavibc_service #content_ifr { height: 200px !important; }
     76        .post-type-creavibc_service #content { min-height: 200px !important; }
     77        .post-type-creavibc_service #postdivrich { margin: 0 !important; }
     78        .post-type-creavibc_service #postdivrich .postbox-header { margin-bottom: 0; }
     79        .post-type-creavibc_service .mce-top-part::before { box-shadow: none; }
     80
     81        /* Make moved title/editor blocks feel tighter */
     82        .post-type-creavibc_service #titlediv { margin: 0 !important; }
     83        .post-type-creavibc_service #titlewrap { margin: 0 !important; }
     84
     85        ' . ( $show_notices ? '' : '
     86        /* Hide most admin notices on Creavi service editor */
     87        .post-type-creavibc_service .notice,
     88        .post-type-creavibc_service div.error,
     89        .post-type-creavibc_service div.updated,
     90        .post-type-creavibc_service .update-nag,
     91        .post-type-creavibc_service .settings-error,
     92        .post-type-creavibc_service .fs-notice,
     93        .post-type-creavibc_service .yoast-notice,
     94        .post-type-creavibc_service .rank-math-notice,
     95        .post-type-creavibc_service .woocommerce-message,
     96        .post-type-creavibc_service .elementor-message {
     97            display: none !important;
     98        }
     99        ' ) . '
     100    </style>';
     101} );
     102
     103
     104/**
     105 * Enqueue admin assets on creavibc_service edit screens
     106 * IMPORTANT: use get_current_screen() instead of relying on global $post
     107 */
     108add_action( 'admin_enqueue_scripts', function( $hook ) {
     109
     110    if ( $hook !== 'post-new.php' && $hook !== 'post.php' ) return;
     111
     112    $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
     113    if ( ! $screen || $screen->post_type !== 'creavibc_service' ) return;
     114
     115    // Best-effort post id (post-new.php may not have $post yet)
     116    $post_id = 0;
     117    if ( isset( $_GET['post'] ) ) {
     118        $post_id = (int) $_GET['post'];
     119    } else {
     120        global $post;
     121        if ( $post && ! empty( $post->ID ) ) {
     122            $post_id = (int) $post->ID;
     123        }
     124    }
     125
     126    // Load local Flatpickr CSS & JS from plugin assets
     127    wp_enqueue_style(
     128        'flatpickr',
     129        CREAVIBC_PLUGIN_URL . 'assets/vendor/flatpickr/flatpickr.min.css',
     130        [],
     131        '4.6.13'
     132    );
     133
     134    wp_enqueue_script(
     135        'flatpickr',
     136        CREAVIBC_PLUGIN_URL . 'assets/vendor/flatpickr/flatpickr.min.js',
     137        [],
     138        '4.6.13',
     139        true
     140    );
     141
     142    // Local admin script
     143    wp_enqueue_script(
     144        'creavibc-admin-js',
     145        CREAVIBC_PLUGIN_URL . 'assets/js/admin.js',
     146        [ 'flatpickr' ],
     147        filemtime( CREAVIBC_PLUGIN_PATH . 'assets/js/admin.js' ),
     148        true
     149    );
     150
     151    wp_enqueue_style(
     152        'creavibc-admin-style',
     153        CREAVIBC_PLUGIN_URL . 'assets/css/admin.css',
     154        [],
     155        filemtime( CREAVIBC_PLUGIN_PATH . 'assets/css/admin.css' )
     156    );
     157
     158    // Busy slots admin
     159    wp_enqueue_script(
     160        'creavibc-gcal-busy-admin',
     161        CREAVIBC_PLUGIN_URL . 'assets/js/cbs-gcal-busy-admin.js',
     162        [ 'jquery' ],
     163        filemtime( CREAVIBC_PLUGIN_PATH . 'assets/js/cbs-gcal-busy-admin.js' ),
     164        true
     165    );
     166
     167    wp_localize_script(
     168        'creavibc-gcal-busy-admin',
     169        'creavibcGcalBusy',
     170        [
     171            'ajaxUrl'   => admin_url( 'admin-ajax.php' ),
     172            'nonce'     => wp_create_nonce( 'creavibc_admin_nonce' ),
     173            'serviceId' => (int) $post_id,
     174        ]
     175    );
     176
     177    // ─────────────────────────────────────────────
     178    // Guided tour / top progress bar (service editor)
     179    // ─────────────────────────────────────────────
     180    wp_enqueue_style(
     181        'creavibc-service-tour',
     182        CREAVIBC_PLUGIN_URL . 'assets/css/service-tour.css',
     183        [],
     184        filemtime( CREAVIBC_PLUGIN_PATH . 'assets/css/service-tour.css' )
     185    );
     186
     187    wp_enqueue_script(
     188        'creavibc-service-tour',
     189        CREAVIBC_PLUGIN_URL . 'assets/js/service-tour.js',
     190        [ 'jquery' ],
     191        filemtime( CREAVIBC_PLUGIN_PATH . 'assets/js/service-tour.js' ),
     192        true
     193    ); 
     194
     195    // Only allow onboarding when parameter is present AND this is a NEW service
     196    $is_onboarding = ( isset($_GET['creavibc_onboarding']) && $_GET['creavibc_onboarding'] === '1' ) ? 1 : 0;
     197
     198    // "new service" = post-new.php OR auto-draft
     199    $is_new_service = 0;
     200
     201    if ( $hook === 'post-new.php' ) {
     202        $is_new_service = 1;
     203    } else {
     204        // post.php editing
     205        if ( $post_id ) {
     206            $p = get_post( $post_id );
     207            if ( $p && $p->post_type === 'creavibc_service' && $p->post_status === 'auto-draft' ) {
     208                $is_new_service = 1;
     209            }
     210        }
     211    }
     212
     213    wp_localize_script(
     214        'creavibc-service-tour',
     215        'CREAVIBC_TOUR',
     216        [
     217            'postId'       => (int) $post_id,
     218            'onboarding'   => $is_onboarding,
     219            'isNewService' => $is_new_service ? 1 : 0, // extra guard for JS
     220            'steps' => [
     221                            [
     222                                'id'    => 'creavibc_service_title_box',
     223                                'label' => __( 'Title', 'creavi-booking-service' ),
     224                                'text'  => __( 'Set the name customers will see when booking.', 'creavi-booking-service' ),
     225                            ],
     226                            [
     227                                'id'    => 'creavibc_service_desc_box',
     228                                'label' => __( 'Description', 'creavi-booking-service' ),
     229                                'text'  => __( 'Add a short summary shown in the booking block.', 'creavi-booking-service' ),
     230                            ],
     231                            [
     232                                'id'    => 'creavibc_available_days',
     233                                'label' => __( 'Available days', 'creavi-booking-service' ),
     234                                'text'  => __( 'Choose which dates can be booked.', 'creavi-booking-service' ),
     235                            ],
     236                            [
     237                                'id'    => 'creavibc_time_slot_grid',
     238                                'label' => __( 'Available time', 'creavi-booking-service' ),
     239                                'text'  => __( 'Select the time slots customers can pick.', 'creavi-booking-service' ),
     240                            ],
     241                            [
     242                                'id'    => 'creavibc_booking_form_fields',
     243                                'label' => __( 'Booking form', 'creavi-booking-service' ),
     244                                'text'  => __( 'Choose what info you collect from customers.', 'creavi-booking-service' ),
     245                            ],
     246                            [
     247                                'id'    => 'creavibc_output_type',
     248                                'label' => __( 'Display type', 'creavi-booking-service' ),
     249                                'text'  => __( 'Show booking as a popup or inline block.', 'creavi-booking-service' ),
     250                            ],
     251                            [
     252                                'id'    => 'creavibc_service_appearance',
     253                                'label' => __( 'Appearance', 'creavi-booking-service' ),
     254                                'text'  => __( 'Customize colors, button text, and styling.', 'creavi-booking-service' ),
     255                            ],
     256                            [
     257                                'id'    => 'creavibc_service_notifications',
     258                                'label' => __( 'Notifications', 'creavi-booking-service' ),
     259                                'text'  => __( 'Configure emails and optional reminders.', 'creavi-booking-service' ),
     260                            ],
     261                            [
     262                                'id'    => 'creavibc_google_calendar',
     263                                'label' => __( 'Google Calendar Integartion', 'creavi-booking-service' ),
     264                                'text'  => __( 'Connect Google Calendar and event settings.', 'creavi-booking-service' ),
     265                            ],
     266                            [
     267                                'id'    => 'submitdiv',
     268                                'label' => __( 'Go to Publish', 'creavi-booking-service' ),
     269                                'text'  => __( 'Publish the service to start accepting bookings.', 'creavi-booking-service' ),
     270                            ],
     271                            ],
     272        ]
     273    );
     274
     275
     276} );
     277
     278
     279/**
     280 * Top progress bar container (always visible)
     281 */
     282add_action( 'edit_form_top', function( $post ) {
     283    if ( ! $post || $post->post_type !== 'creavibc_service' ) return;
     284
     285    echo '<div id="creavibc-tour-bar" class="creavibc-tour-bar" aria-label="' . esc_attr__( 'Service setup steps', 'creavi-booking-service' ) . '"></div>';
     286} );
     287
     288/**
     289 * Prefill default description for new Service
     290 */
     291add_filter( 'default_content', function( $content, $post ) {
     292
     293    if ( ! $post || $post->post_type !== 'creavibc_service' ) {
     294        return $content;
    62295    }
    63 });
    64 
    65 
     296
     297    // Only on brand new service (not editing existing)
     298    if ( $post->post_status !== 'auto-draft' ) {
     299        return $content;
     300    }
     301
     302    // If something already exists, don't override
     303    if ( ! empty( $content ) ) {
     304        return $content;
     305    }
     306
     307    $default =
     308        '<p><strong>' . __( 'About this service', 'creavi-booking-service' ) . '</strong></p>' .
     309        '<p>' . __( 'Describe what this service includes, who it is for, and what customers can expect during the appointment.', 'creavi-booking-service' ) . '</p>' .
     310        '<ul>' .
     311            '<li>' . __( 'Duration and format', 'creavi-booking-service' ) . '</li>' .
     312            '<li>' . __( 'Preparation instructions (if any)', 'creavi-booking-service' ) . '</li>' .
     313            '<li>' . __( 'What happens after booking', 'creavi-booking-service' ) . '</li>' .
     314        '</ul>';
     315
     316    return $default;
     317
     318}, 10, 2 );
     319
     320add_action( 'post_submitbox_misc_actions', 'creavibc_metrics_optin_checkbox_in_publish_box' );
     321function creavibc_metrics_optin_checkbox_in_publish_box() {
     322    global $post;
     323    if ( ! $post || $post->post_type !== 'creavibc_service' ) return;
     324
     325    $optin = get_option( 'creavibc_metrics_optin', '1' ); // default checked
     326    wp_nonce_field( 'creavibc_metrics_optin_save', 'creavibc_metrics_optin_nonce' );
     327
     328    ?>
     329    <div class="misc-pub-section creavibc-metrics-optin" style="padding-top:10px;">
     330        <label style="display:block; line-height:1.35;">
     331            <input type="checkbox" name="creavibc_metrics_optin" value="1" <?php checked( $optin, '1' ); ?> />
     332            <strong><?php esc_html_e( 'Help improve Creavi Booking Service', 'creavi-booking-service' ); ?></strong><br>
     333            <span style="color:#646970;">
     334                <?php esc_html_e( 'Send anonymous usage data.', 'creavi-booking-service' ); ?>
     335            </span>
     336        </label>
     337    </div>
     338    <?php
     339}
  • creavi-booking-service/trunk/includes/ajax-handlers.php

    r3460766 r3468659  
    355355            '{admin_link}'      => admin_url( "post.php?post=$service_id&action=edit" ),
    356356            '{google_calendar}' => $gcal_url,
     357            '{name_url}'        => rawurlencode( (string) $name ),
    357358        ];
    358359        $replacements_admin = $replacements_user;
     
    367368            '{admin_link}'      => admin_url( "post.php?post=$service_id&action=edit" ),
    368369            '{google_calendar}' => $gcal_url,
     370            '{name_url}'        => rawurlencode( (string) $name ),
    369371        ];
    370372        $replacements_admin = $replacements_user;
  • creavi-booking-service/trunk/includes/meta-boxes.php

    r3460766 r3468659  
    22if ( ! defined( 'ABSPATH' ) ) exit;
    33
    4 
    54add_action('add_meta_boxes', function () {
    6 
    7     add_meta_box('creavibc_output_type', __( 'Booking Display Type', 'creavi-booking-service' ), 'creavibc_render_output_type_box', 'creavibc_service', 'normal', 'high');
    8 
    9     add_meta_box('creavibc_available_days', __( 'Available Booking Days', 'creavi-booking-service' ), 'creavibc_render_available_days_box', 'creavibc_service', 'normal', 'high');
    10 
    11     add_meta_box('creavibc_time_slot_grid', __( 'Visual Time Slot Selector', 'creavi-booking-service' ), 'creavibc_render_weekday_slots_grid_box', 'creavibc_service', 'normal', 'high');
    12 
    13     add_meta_box('creavibc_booking_form_fields', __( 'Booking Form Fields', 'creavi-booking-service' ), 'creavibc_render_form_fields_box', 'creavibc_service', 'normal', 'default');
    14 
    15     add_meta_box('creavibc_service_appearance', __( 'Booking Appearance', 'creavi-booking-service' ), 'creavibc_render_service_appearance_box', 'creavibc_service', 'normal', 'default');
    16 
    17     add_meta_box('creavibc_service_notifications', __( 'Notifications', 'creavi-booking-service' ), 'creavibc_render_service_notifications_box', 'creavibc_service', 'normal', 'default');
    18 
    19     add_meta_box('creavibc_google_calendar', __( 'Google Calendar', 'creavi-booking-service' ), 'creavibc_render_google_calendar_box', 'creavibc_service', 'normal', 'low');
    20 
    21     add_meta_box('creavibc_booking_details', __( 'Booking Details', 'creavi-booking-service' ), 'creavibc_render_booking_meta', 'creavibc_booking', 'normal', 'default');
    22 
     5   
     6    // CREAVIBC SERVICE (main column)   
     7
     8    add_meta_box(
     9        'creavibc_available_days',
     10        __( 'Available Booking Days', 'creavi-booking-service' ),
     11        'creavibc_render_available_days_box',
     12        'creavibc_service',
     13        'normal',
     14        'high'
     15    );
     16
     17    add_meta_box(
     18        'creavibc_time_slot_grid',
     19        __( 'Visual Time Slot Selector', 'creavi-booking-service' ),
     20        'creavibc_render_weekday_slots_grid_box',
     21        'creavibc_service',
     22        'normal',
     23        'high'
     24    );
     25
     26    add_meta_box(
     27        'creavibc_output_type',
     28        __( 'Booking Display Type', 'creavi-booking-service' ),
     29        'creavibc_render_output_type_box',
     30        'creavibc_service',
     31        'normal',
     32        'default'
     33    );
     34
     35    add_meta_box(
     36        'creavibc_booking_form_fields',
     37        __( 'Booking Form Fields', 'creavi-booking-service' ),
     38        'creavibc_render_form_fields_box',
     39        'creavibc_service',
     40        'normal',
     41        'default'
     42    );
     43
     44    add_meta_box(
     45        'creavibc_service_appearance',
     46        __( 'Booking Appearance', 'creavi-booking-service' ),
     47        'creavibc_render_service_appearance_box',
     48        'creavibc_service',
     49        'normal',
     50        'default'
     51    );
     52
     53    add_meta_box(
     54        'creavibc_service_notifications',
     55        __( 'Notifications', 'creavi-booking-service' ),
     56        'creavibc_render_service_notifications_box',
     57        'creavibc_service',
     58        'normal',
     59        'default'
     60    );
     61
     62    // Google Calendar should be LAST in main column
     63    add_meta_box(
     64        'creavibc_google_calendar',
     65        __( 'Google Calendar', 'creavi-booking-service' ),
     66        'creavibc_render_google_calendar_box',
     67        'creavibc_service',
     68        'normal',
     69        'low'
     70    );
     71   
     72    // CREAVIBC BOOKING (other CPT) 
     73    add_meta_box(
     74        'creavibc_booking_details',
     75        __( 'Booking Details', 'creavi-booking-service' ),
     76        'creavibc_render_booking_meta',
     77        'creavibc_booking',
     78        'normal',
     79        'default'
     80    );
     81
     82     $screen = 'creavibc_service';
     83
     84    add_meta_box(
     85        'creavibc_shortcode_box',
     86        __( 'Shortcode', 'creavi-booking-service' ),
     87        'creavibc_render_shortcode_metabox',
     88        $screen,
     89        'side',
     90        'low' // low => usually appears AFTER Publish box
     91    );
     92
     93}, 20);
     94
     95/**
     96 * Force metabox order on creavibc_service edit screen:
     97 */
     98
     99add_filter('get_user_option_meta-box-order_creavibc_service', function($order) {
     100
     101    // Always enforce our default order (clean layout for services)
     102    $normal = implode(',', [
     103        'creavibc_available_days',
     104        'creavibc_time_slot_grid',
     105        'creavibc_output_type',
     106        'creavibc_booking_form_fields',
     107        'creavibc_service_appearance',
     108        'creavibc_service_notifications',
     109        'creavibc_google_calendar',
     110    ]);
     111
     112    // Right column: Featured Image first, then Publish (as you requested)
     113    $side = implode(',', [
     114        'postimagediv',
     115        'submitdiv',
     116    ]);
     117
     118    return [
     119        'normal'   => $normal,
     120        'side'     => $side,
     121        'advanced' => '',
     122    ];
    23123});
     124
    24125
    25126
     
    41142});
    42143
     144function creavibc_render_shortcode_metabox( $post ) {
     145
     146    $post_id = isset( $post->ID ) ? (int) $post->ID : 0;
     147
     148    echo '<div class="creavibc-shortcode-box">';
     149
     150    if ( $post_id <= 0 || 'auto-draft' === $post->post_status ) {
     151        echo '<p class="description">' .
     152            esc_html__( 'Save the service first to generate the shortcode.', 'creavi-booking-service' ) .
     153        '</p>';
     154        echo '</div>';
     155        return;
     156    }
     157
     158    $output_type = get_post_meta( $post_id, '_creavibc_output_type', true );
     159    $output_type = is_string( $output_type ) ? sanitize_key( $output_type ) : '';
     160
     161    $shortcode_tag = ( 'inline' === $output_type ) ? 'creavibc_booking_inline' : 'creavibc_booking_button';
     162    $shortcode     = '[' . $shortcode_tag . ' id="' . $post_id . '"]';
     163
     164    $code_id = 'creavibc-shortcode-code-' . $post_id;
     165
     166    $title = ( 'inline' === $output_type )
     167        ? esc_html__( 'Inline booking shortcode', 'creavi-booking-service' )
     168        : esc_html__( 'Popup booking shortcode', 'creavi-booking-service' );
     169
     170    $desc = esc_html__( 'Paste this into any page/post (Shortcode block / Elementor Shortcode widget):', 'creavi-booking-service' );
     171
     172    echo '<p class="creavibc-sc-title">' . esc_html( $title ) . '</p>';
     173    echo '<p class="description creavibc-sc-desc">' . esc_html( $desc ) . '</p>';
     174
     175    echo '<div class="creavibc-sc-fieldwrap">';
     176    echo '  <input type="text" class="creavibc-sc-input" id="' . esc_attr( $code_id ) . '" value="' . esc_attr( $shortcode ) . '" readonly spellcheck="false" />';
     177    echo '</div>';
     178
     179    echo '<div class="creavibc-sc-actions">';
     180    echo '  <button type="button" class="button button-secondary creavibc-sc-copy" data-copy-target="' . esc_attr( $code_id ) . '">';
     181    echo '    <span class="creavibc-sc-copy-default">' . esc_html__( 'Copy shortcode', 'creavi-booking-service' ) . '</span>';
     182    echo '    <span class="creavibc-sc-copy-done" aria-hidden="true">' . esc_html__( 'Copied', 'creavi-booking-service' ) . '</span>';
     183    echo '  </button>';
     184    echo '</div>';
     185
     186    echo '</div>';
     187}
    43188
    44189function creavibc_render_output_type_box($post) {
     
    84229    $mode = get_post_meta($post->ID, '_creavibc_availability_mode', true);
    85230    if ( ! in_array($mode, ['static', 'dynamic'], true) ) {
    86         $mode = 'static';
     231        $mode = 'dynamic';
    87232    }
    88233
    89234    $months_ahead = (int) get_post_meta($post->ID, '_creavibc_dynamic_months_ahead', true);
    90     $months_ahead = max(1, min(12, $months_ahead ?: 1));
     235    $months_ahead     = (int) $months_ahead_raw;
     236    if ( $months_ahead <= 0 ) {
     237        $months_ahead = 1; // default: 1 month ahead
     238    }
     239    $months_ahead = max( 1, min( 12, $months_ahead ) );
    91240
    92241    // Canonical excluded field name + meta key (do not change!)
     
    648797    echo '<textarea name="creavibc_email_user_template" id="creavibc_email_user_template" rows="4" style="width: 100%;">' . esc_textarea($user_email_tpl) . '</textarea></p>';
    649798
    650     echo '<p class="description">' . esc_html__( 'Available tags:', 'creavi-booking-service' ) . ' {name}, {email}, {date}, {time}, {service}, {custom} <span style="color:#666;">' . esc_html__( '- submitted custom fields', 'creavi-booking-service' ) . '</span></p>';
     799    echo '<p class="description">' . esc_html__( 'Available tags:', 'creavi-booking-service' ) . ' {name}, {name_url}, {email}, {date}, {time}, {service}, {custom} <span style="color:#666;">' . esc_html__( '- submitted custom fields', 'creavi-booking-service' ) . '</span></p>';
    651800
    652801
     
    715864
    716865    echo '<p class="description">';
    717     echo esc_html__( 'Available tags:', 'creavi-booking-service' ) . ' {name}, {email}, {date}, {time}, {service}, {custom} ';
     866    echo esc_html__( 'Available tags:', 'creavi-booking-service' ) . ' {name}, {name_url}, {email}, {date}, {time}, {service}, {custom} ';
    718867    echo '<span style="color:#666;">' . esc_html__( '- submitted custom fields', 'creavi-booking-service' ) . '</span>';
    719868    echo '</p>';
  • creavi-booking-service/trunk/includes/reminders.php

    r3441012 r3468659  
    121121    $replacements = [
    122122        '{name}'    => $name,
     123        '{name_url}' => rawurlencode( (string) $name ),
    123124        '{email}'   => $email,
    124125        '{date}'    => $display_date,
  • creavi-booking-service/trunk/includes/save-service.php

    r3456986 r3468659  
    66    if (!current_user_can('edit_post', $post_id)) return;
    77
     8    // ─────────────────────────────────────────────
     9    // Metrics opt-in (Publish box) + publish event
     10    // ─────────────────────────────────────────────
     11    $metrics_nonce = isset($_POST['creavibc_metrics_optin_nonce'])
     12        ? sanitize_text_field(wp_unslash($_POST['creavibc_metrics_optin_nonce']))
     13        : '';
     14
     15    if ( $metrics_nonce && wp_verify_nonce( $metrics_nonce, 'creavibc_metrics_optin_save' ) ) {
     16
     17        // Save opt-in (default checked in UI)
     18        $optin = isset($_POST['creavibc_metrics_optin']) ? '1' : '0';
     19        update_option( 'creavibc_metrics_optin', $optin, false );
     20
     21        // If opted in, fire publish events only once per service
     22        if ( $optin === '1' ) {
     23
     24            // Check current status reliably
     25            $post = get_post( $post_id );
     26            $is_publish = ( $post && $post->post_status === 'publish' );
     27
     28            // Only send once per service
     29            $already_sent = get_post_meta( $post_id, '_creavibc_metrics_published_sent', true );
     30
     31            if ( $is_publish && $already_sent !== '1' ) {
     32
     33                // Backfill "plugin_activated" once (first legal moment to send)
     34                $activated_at = (int) get_option( 'creavibc_activated_at', 0 );
     35                if ( $activated_at > 0 && function_exists( 'creavibc_metrics_send_event' ) ) {
     36                    creavibc_metrics_send_event( 'plugin_activated', [
     37                        'activated_at' => $activated_at,
     38                    ] );
     39                    delete_option( 'creavibc_activated_at' );
     40                }
     41
     42                if ( function_exists( 'creavibc_metrics_send_event' ) ) {
     43                    creavibc_metrics_send_event( 'service_published', [
     44                        'service_id' => (int) $post_id,
     45                    ] );
     46                }
     47
     48                update_post_meta( $post_id, '_creavibc_metrics_published_sent', '1' );
     49            }
     50        }
     51    }
     52
     53
     54
    855    // Save available days   
    9    
    10 
    1156    $days_nonce = isset($_POST['creavibc_days_nonce']) ? sanitize_text_field(wp_unslash($_POST['creavibc_days_nonce'])) : '';
    1257    if ( $days_nonce && wp_verify_nonce( $days_nonce, 'creavibc_save_days' ) ) {
  • creavi-booking-service/trunk/readme.txt

    r3460772 r3468659  
    55Tested up to: 6.9
    66Requires PHP: 7.4 
    7 Stable tag: 1.1.9
     7Stable tag: 1.2.0
    88License: GPLv2 or later 
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    9999== Changelog ==
    100100
     101= 1.2.0 =
     102* Added `{name_url}` template tag for Booking emails, Reminder emails.
     103* Added Service setup onboarding navigation bar for a clearer configuration flow.
     104* Improved Service editor layout with structured top navigation
     105
    101106= 1.1.9 =
    102107* Added per-service Google Calendar connections – each service can now connect to its own Google account.
Note: See TracChangeset for help on using the changeset viewer.