Статьи

Как модернизировать систему бронирования с помощью Acuity Scheduling

Эта статья была спонсирована Acuity Scheduling . Спасибо за поддержку спонсоров, которые делают возможным использование SitePoint.

Карты на столе: с тех пор, как я научился водить машину, прошло довольно много времени. Я не могу вспомнить, как я нашла своего инструктора, но я почти уверен, что это было не через веб-сайт. Что я знаю, так это то, что бронирование урока включало телефонный звонок. С моим инструктором в дороге весь день, этот звонок обычно приходился на нерабочее время. Сами встречи были запланированы вручную в старом добром дневнике.

Я не уверен, что такой подход уменьшит это сейчас. Я знаю, что если бы я искал, это было бы через Интернет, и, как и большинство людей, я начал воспринимать это как должное, что я могу бронировать вещи онлайн.

Как насчет другой стороны медали? Такое управление уроками — неэффективный подход. Я могу только догадываться, сколько раз мой преподаватель приходил на урок, чтобы обнаружить, что студент оставил на своем автоответчике сообщение об отмене у себя дома.

В этом уроке мы собираемся перетащить нашего инструктора по вождению в 21-й век, предоставив веб-виджет для просмотра доступности и планирования уроков. Кроме того, мы воспользуемся программой Acuity Scheduling, чтобы помочь им управлять своим дневником, не прибегая к ручке и бумаге.

Прежде чем продолжить, вы можете посмотреть, что мы собираемся создать с помощью онлайн-демонстрации или снимка экрана ниже.

Система бронирования демо

Код для этого урока доступен на GitHub .

Глядя на бизнес

Прежде чем углубиться в реализацию, давайте немного подробнее рассмотрим бизнес работы в качестве инструктора по вождению.

У инструктора по вождению, конечно, есть несколько «слотов», доступных в данный день. Уроки вождения по самой своей природе индивидуальны, что немного упрощает для нас вещи.

Чтобы все было относительно просто, мы замаскируем некоторые практические аспекты. Например, в реальном мире было бы невозможно начать урок на одной стороне данного города в 3 часа дня, когда другой закончил несколько минут раньше на другой стороне города.

В нашем виджете доступности будет отображаться календарь на данный месяц (по умолчанию текущий), который наглядно показывает, какие дни доступны. Если пользователь нажимает на определенный день, он будет детализировать и отображать временные интервалы, доступные для этого дня.

Мы сделаем это, используя некоторые из новейших интерфейсных технологий, что сделает наш виджет очень отзывчивым.

Зачем создавать пользовательский виджет?

Существует ряд возможных причин, по которым вы можете решить создать собственный виджет, например:

  • Вы можете стилизовать его так, чтобы он гармонично сочетался с дизайном вашего сайта.
  • Вы можете использовать любые передние и внутренние технологии, которые у вас уже есть.
  • Пользовательский виджет может быть наиболее подходящим решением, если вы предпочитаете принимать платежи самостоятельно
  • Вы можете держать людей на своем сайте, поскольку они завершают процесс планирования

Пример кода

Код, сопровождающий это руководство, доступен на GitHub . Для краткости, мы не будем охватывать весь код здесь, поэтому вам нужно взять копию независимо от того, кодируете ли вы или просто читаете по ходу дела.

Пример включает в себя серверную часть PHP вместе с виджетом бронирования; Через некоторое время мы увидим, почему нам нужен серверный компонент.

Самый простой способ запустить пример — использовать встроенный веб-сервер PHP.

Просто cdpublic

 php -S localhost:8000

Представляем Acuity Scheduling

Acuity Scheduling — это онлайн-сервис для управления и планирования встреч. В контексте нашего инструктора по вождению назначение — это урок вождения, но его применение гораздо шире.

Одной из ключевых функций Acuity является возможность предоставлять информацию о доступности в режиме реального времени. Мы можем получить эту информацию с помощью API, чтобы вставить ее на веб-сайт нашего инструктора по вождению, а затем использовать ее в качестве основы нашего виджета планирования.

Acuity предлагает целый ряд других функций, в том числе возможность принимать онлайн-платежи, управлять клиентами, отправлять электронную почту и текстовые сообщения, напоминания и многое другое.

Acuity Scheduling API

Мы собираемся интегрироваться с Acuity, используя их API. В частности, мы будем использовать три конечные точки:

GET /availability/dates

GET /availability/times

POST /appointments

Вместе первые две конечные точки предоставляют достаточно информации для конечного пользователя, чтобы сразу увидеть, что доступно, чтобы они могли выбрать подходящий слот. Как только у нас будет доступная дата и время, мы можем собрать некоторую информацию от ученика и назначить встречу для него.

Аутентификация

Acuity API предоставляет два метода аутентификации: OAuth2 и базовая аутентификация HTTP. Первый действительно применим, когда вы имеете дело с несколькими пользователями Acuity. Для наших целей это, вероятно, излишне.

Однако для использования базовой HTTP-аутентификации нам понадобится прокси-вызовы API через наш сервер.

Причина этого проста. Обычная HTTP-аутентификация требует имени пользователя и пароля, которые, если бы мы вызывали API напрямую из нашего кода JavaScript, были бы открыты. При использовании Acuity API имя пользователя и пароль — это ваш идентификатор пользователя и ключ API соответственно, но принцип в точности совпадает. Это создает очевидную угрозу безопасности.

Ключевая часть серверного кода для определения доступности, которую вы найдете в репозитории, сопровождающем эту статью , заключается в следующем:

 
$app->get( 'api/availability/{period}', function( Request $request, $period ) use ( $app ) {

    // Build the URL, incorporating the `appointmentTypeID`.
    $query = $request->query->all( ) + [ 'appointmentTypeID' => $app[ 'appointmentTypeID' ] ];  
    $url = sprintf( '/availability/%s?%s', $period, http_build_query( $query ) );

    // Make the request...
    $response = $app[ 'client' ]->request( $url );		

    // If there's an error, write it to the log
    if ( $response[ 'status_code' ] !== 200 ) {
        $app[ 'logger' ]->error( $response[ 'message' ] );
    }

    // ... and simply return it
    return json_encode( $response );

} )
->assert( 'period', 'dates|times');

Код использует официальный PHP SDK . Проверьте репозиторий, чтобы увидеть, как создается клиент, или ознакомьтесь с документацией SDK и документацией разработчика .

Хотя этот код несколько упрощен, для целей этой демонстрации он должен сработать. По сути, все, что он делает — это «передает» вызовы либо /availability/dates/availability/times Он также объединяет идентификатор типа встречи, который в основном сообщает Acuity, для чего мы пытаемся найти доступность.

Таким образом, мы можем сохранить наш ключ API — который действует как пароль — безопасно на сервере, не раскрывая его в нашем клиентском коде.

Если вы не хотите использовать PHP, вам не нужно. Существует официальный SDK для Node.js , или, если вы используете что-то еще, тогда будет просто вызвать API с вашего языка программирования по вашему выбору. Обратитесь к документации для получения дополнительной информации.

Без дальнейших церемоний, давайте начнем.

Начало настройки

Для работы с этим руководством вам понадобится аккаунт в Acuity Scheduling. Зайдите на сайт, чтобы зарегистрироваться, что займет всего пару минут.

14-дневная бесплатная пробная версия отлично подойдет для целей этого урока, поэтому она не должна стоить вам ни копейки.

Если у вас есть учетная запись, вам нужно настроить новый тип встречи. Для целей этого урока это довольно просто — у нас только один тип встреч, и это урок вождения.

Зайдя в Acuity, перейдите в « Бизнес-настройки»> «Типы встреч» и нажмите « Новый тип услуг» , затем заполните необходимую информацию. Вероятно, проще всего сохранить продолжительность в один час, что, вероятно, то, что вы ожидаете от урока вождения.

Там есть дополнительная помощь, если вам это нужно.

Если вы нажмете на новый тип встречи и посмотрите на URL, вы увидите, что он содержит уникальный идентификатор. Запишите это, потому что вам нужно будет добавить его в файл конфигурации через минуту.

Типы встреч Acuity

Следующим шагом должно стать определение вашей доступности. По сути, именно здесь вы определяете свое рабочее время, будь то обычный понедельник-пятница 9–6 или что-то более нерегулярное. Вы можете сделать это, выбрав Доступность в Бизнес-настройках .

В целях примера виджета, который мы создаем, вероятно, проще всего, если вы установите часы на 9–6, оставив перерыв на обед.

На приведенном ниже снимке экрана показан пример рабочего времени с 9 до 18 часов, с обедом в 12 часов.

Рабочее время

Acuity обеспечит соответствие встреч в рабочее время, поэтому, если вы укажете их с 9:00 до 18:00, последний доступный интервал для часового урока будет в 17:00. Для дня, в котором есть все доступные слоты, наш виджет будет выглядеть как на скриншоте ниже.

Ежедневные слоты доступны

Следующим шагом является выяснение ваших учетных данных API. Перейдите в « Бизнес-настройки» → «Интеграции» , прокрутите страницу вниз и нажмите «Просмотреть свои учетные данные API» .

Теперь, когда у вас есть три части необходимой информации — ваш идентификатор пользователя, ключ API и новый идентификатор типа встречи — пришло время добавить это в файл конфигурации.

В репозитории вы найдете файл с именем config/__COPY_ME__.json Возьмите его копию, назвав его config/dev.json

Часть dev указывает, что мы работаем в среде разработки. В производстве вы, вероятно, назвали бы это production.json

Заполните этот новый файл конфигурации, заменив соответствующие записи:

 
{  
  "debug": true,
  "userId": "YOUR-USER-ID",
  "apiKey": "YOUR-API-KEY",
  "appointmentTypeID": "APPOINTMENT-TYPE-ID"
}

С этой настройкой наш простой бэкэнд сможет связываться с API Acuity, чтобы находить доступность и планировать встречи.

Обратите внимание, что код PHP, который сопровождает эту статью, включает в себя очень простую регистрацию; если он получит ответ об ошибке от API Acuity, он зарегистрирует соответствующее сообщение в logs/error.log

Начиная с реализации

Мы собираемся использовать Vue.js для сборки самого виджета. Во многом это похоже на урезанную версию Angular, специально для создания реактивных компонентов. Если вы предпочитаете использовать что-то другое, например, Angular, то общие принципы не должны быть такими уж разными.

Мы также будем использовать Moment.js для упрощения работы с датами и временем, Vue Resource для связи с нашим бэкэндом и Vue Form для простой проверки формы.
Установите зависимости:

 bower install vue moment vue-resource vue-form --save

Вы можете использовать npm, если хотите. Просто измените пути соответственно.

Теперь давайте создадим базовую структуру страницы. В примере используется Twig, а HTML- views/index.twig.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">

<title>Book a Driving Lesson</title>

<link rel=»stylesheet» href=»https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css»>
<link rel=»stylesheet» href=»/assets/styles.css»>

</head>

<body>

<script src=»/bower_components/moment/moment.js»></script>
<script src=»/bower_components/vue/dist/vue.js»></script>
<script src=»bower_components/vue-resource/dist/vue-resource.js»></script>
<script src=»/bower_components/vue-form/vue-form.js»></script>
<script src=»/assets/scripts.js»></script>

</body>
</html>

 public/assets/scripts.js

Файл
Vue.http.options.root = '/api';

Vue.filter(‘time’, function ( hour ) {
if ( hour < 10 ) {
return ‘0’ + hour + ‘:00’;
}
return hour + ‘:00’;
});

Vue.filter(‘formatDateTime’, function( day, format ) {
if ( ! day ) {
return »;
}
return day.format( format )
});

var widget = new Vue({
el : ‘#availability-widget’,
data : {
// . . .
},
ready : function() {
// . . .
},
methods : {
// . . .
}
});

 ready()

По мере заполнения мы будем заполнять следующие три части виджета:

  • Свойство data используется для инициализации наших данных. Указание здесь свойств гарантирует, что Vue.js выполняет процесс получения / установки во время инициализации.
  • Метод methods
  • Как вы можете ожидать, методы — это хеш методов; мы определим их количество, чтобы пользователь мог выбрать дату и завершить бронирование.
  • Вы можете получить копию таблицы стилей из репозитория или создать свою собственную, соответствующую внешнему виду вашего веб-сайта.

Один шаг за раз

Наш виджет будет состоять из трех «этапов»:

  1. Пользователь выбирает дату с некоторой доступностью в мини-календаре.
  2. Выбрав дату, они выбирают «слот» из доступных.
  3. После того как дата и время выбраны, мы покажем их пользователю для подтверждения и примем некоторую основную информацию. Как только они предоставят эту информацию, мы можем вызвать API, чтобы фактически запланировать урок.

Давайте начнем с построения HTML-кода для трех шагов:

 
<div id="availability-widget">
    <ol class="steps">
        <li class="col-md-4 active">
            <header>Select a Day</header>
        </li>
        <li class="col-md-4" v-bind:class="{ 'active' : day }">
            <header>Choose a Time</header>
        </li>
        <li class="col-md-4" v-bind:class="{ 'active' : time }">
            <header>Confirm Booking</header>
        </li>
    </ol>
</div>

Обратите внимание, что мы завернули шаги в <div>availability-widget Это соответствует свойству el в нашем коде виджета, что означает, что Vue будет привязываться к нему для реализации нашего виджета.

Мы добавим разметку для каждого шага в ближайшее время, но сейчас обратите внимание, что мы используем класс active используя v-binddaytime

Инициализация данных

Мы собираемся сохранить три отдельных свойства данных, чтобы помочь отслеживать процесс «детализации» до определенной даты и времени:

  • month
  • day
  • time

Вы можете задаться вопросом, почему мы поддерживаем три отдельных свойства даты, учитывая, что экземпляр Moment может инкапсулировать все три части. Мы делаем это так, чтобы мы могли отслеживать, что конкретно выбрал пользователь.

Давайте инициализируем наши данные:

 
data : {
    today : moment(),
    weekdays : moment.weekdaysShort(),      
    calendar : [],
    month : moment(),
    day : null,
    time : null,
    hours : [],
    bookingForm : {},
    learner : {
        firstName : null,
        lastName : null,
        email : null
    },
    bookingStatus : 'pending'
}

Получая названия дней недели от Moment, мы потенциально делаем еще проще упростить интернационализацию нашего виджета.

Здесь мы также инициализируем свойства для хранения определения календаря, рабочего набора часов, данных обучаемого, формы бронирования и статуса части процесса бронирования.

В поисках доступного дня

Первая часть заключается в вызове API Acuity для определения доступности на определенный месяц и создания мини-календаря. Когда компонент инициализируется, мы назовем его для текущего месяца, но нам также нужно будет разрешить пользователям перемещаться между месяцами, используя стрелку в заголовке календаря. Это показано на скриншоте ниже.

Мини календарь

Давайте начнем с HTML:

 
<li class="col-md-4 active">
    <header>Select a Day</header>
    <div class="cal">
        <div class="header">
            <span class="left button" id="prev" v-on:click="previousMonth" v-if="!month.isSame(today, 'month')"> ⟨ </span>                     
            <span class="month-year" id="label"> {{ month | formatDateTime 'MMM YYYY' }} </span>                        
            <span class="right button" id="next" v-on:click="nextMonth"> ⟩ </span>
        </div>
        <table id="days">
            <tr>
                <td v-for="weekday in weekdays">{{ weekday }}</td>
            </tr>
        </table>
        <div class="cal-frame" v-if="calendar.length">                                  
            <table class="curr">
                <tr v-for="row in calendar">
                    <td v-for="day in row" v-bind:class="{ 'nil' : !day.d, 'today' : day.today, 'past' : day.past, 'available' : day.available }" v-on:click="selectDay(day.d)">{{ day.d }}</td>
                </tr>                                       
            </table>
        </div>
	    <div class="loading" v-if="!calendar.length">
			<p><img src="/assets/ajax-loader.gif"></p>
			<p>Loading dates...</p>
	    </div>
    </div>
</li>

Здесь стоит отметить пару вещей.

Заголовок календаря содержит кнопки для навигации по месяцам. Видимость кнопки «Назад» зависит от того, отображает ли календарь текущий месяц.

Каждой ячейке (например, день) в календаре назначен один или несколько классов CSS. Ячейки, составляющие календарь, могут иметь одно или несколько из следующих «состояний»:

  • Пустая ячейка, используемая для добавления ее в табличную структуру.
  • День в прошлом.
  • Текущий день
  • День с доступностью.
  • День без доступности.

Наконец, обратите внимание, что мы назначаем обработчик кликов для дня, который будет вызывать метод selectDay() Мы реализуем это в следующем разделе; но сначала нам нужно заполнить и построить календарь.

Итак, нам нужно получить доступность за данный месяц. Вот как будет выглядеть вывод из /availability/dates

 
[
  {
    "date": "2016-06-02"
  },
  {
    "date": "2016-06-06"
  },
  ...
}

Имея это в виду, давайте создадим наш первый метод виджетов, который отвечает за получение доступности за данный месяц. Помните, что это относится к части methods

 
methods : {

    /**
     * Fetch the availability data for a given month
     */
    getAvailabilityForMonth : function( ) {
        
        this.calendar = [];

        this.$http.get( 'availability/dates', { month : this.month.format( 'YYYY-MM' ) } ).then( function( response ) {          
                
            this.days = [];

            var available = response.data.map( function( item ) {
                return moment( item.date ).date( );
            });             

            var temp = moment( this.month ).date( 1 );              
            var m = temp.month();
            var now = moment();

            do {                    
                this.days.push({
                    d : temp.date(),
                    past : temp.isBefore( now, 'day' ),
                    today : temp.isSame( now, 'day' ),
                    available : ( available.indexOf( temp.date() ) > -1 ),
                });                 
                temp.add( 1, 'day' );
            } while ( temp.month() == m );

            this.buildCalendar();

        });
    },

    // . . .
}

Массива дней месяца недостаточно для создания календаря. В начале нам понадобится несколько дополнительных отступов, в зависимости от дня недели, на который приходится первое число месяца. Затем нам нужно разбить его на строки, добавив в конце дополнительные ячейки, чтобы в каждой строке было ровно семь ячеек.

Код для построения таблицы календаря немного скучный, чтобы вдаваться в подробности, но, по сути, он делит дни в месяце на строки, а затем добавляет «заполнение» с обеих сторон, так что первое число месяца установите соответствующий день, и каждая строка содержит семь ячеек. Рассматриваемый метод — buildCalendar()

Помните, что мы хотим загрузить доступность текущего месяца при инициализации компонента. Мы можем сделать это в ready

 
var widget = new Vue({
    // . . .
    ready: function() {        
      this.getAvailabilityForMonth();
    },
    // . . .
 });

Также в разделе методов нам понадобится следующее для реализации стрелок вперед и назад в мини-календаре:

 
previousMonth : function() {            
    var current = this.month;
    this.$set( 'month', null )
    this.$set( 'month', current.subtract( 1, 'months' ) );      
    this.getAvailabilityForMonth(); 
},

nextMonth : function() {            
    var current = this.month;
    this.$set( 'month', null )
    this.$set( 'month', current.add( 1, 'months' ) );           
    this.getAvailabilityForMonth();
},

Теперь у нас есть мини-календарь, в котором отмечены дни, которые есть в наличии, что позволяет пользователям углубляться в детали, выбирая время для своего урока.

Нахождение доступного времени

На данный момент у нас есть определенный день, который, как мы знаем, имеет как минимум один доступный слот. Следующий шаг, затем, состоит в том, чтобы перечислить эти места и позволить ученику выбрать один.

Напоминание о том, как будет выглядеть этот раздел виджета, показано на скриншоте ниже.

Ежедневные слоты доступны

Вы помните, что нажатие на день в календаре вызывает метод selectDay()

Прежде чем мы реализуем это, вот как будет выглядеть вывод из /availability/times

 
[
  {
    "time": "2016-06-02T13:00:00-0800"
  },
  {
    "time": "2016-06-04T14:00:00-0800"
  },
  ...
}

Давайте начнем с преобразования этого в массив доступных времен, извлекая час:

 
var available = response.data.map( function( item ) {
    return moment( item.time ).hour( );
});

Теперь мы можем создать цикл, который проходит по часам в дне, и установить флаг, чтобы указать, доступен ли этот слот:

 
for ( var h = 7; h  -1 )
    });
}

Вот метод selectDay()

 
selectDay : function( d ) {
    if ( d ) {
        // Create am instance
        var day = moment( this.month ).date ( d );

        // If it's in the past, exit
        if ( day.isBefore( moment(), 'day' ) ) {
            return;
        }

        this.day = day;
        this.hours = [];

        this.$http.get( 'availability/times', { date : this.day.format( 'YYYY-MM-DD' ) } ).then( function( response ) {          
            
            var available = response.data.map( function( item ) {
                return moment( item.time ).hour( );
            });

            for ( var h = 7; h  -1 )
                });
            }

        });
        
        this.time = null;
    }
},

Теперь давайте обратим наше внимание на HTML:

 
<li class="col-md-4" v-bind:class="{ 'active' : day }">
	<header>Choose a Time</header>
	<ol class="times" v-if="day">
		<li class="header">
			{{ day | formatDateTime 'ddd Do MMM YYYY' }}
		</li>
		<li v-for="hour in hours" v-bind:class="{ 'available' : hour.available }" v-on:click="selectTime( hour )">
			<div class="hour">{{ hour.h | time }}</div>
		</li>
	</ol>
	<div class="loading" v-if="day && !hours.length">
		<p><img src="/assets/ajax-loader.gif"></p>
		<p>Loading times...</p>
	</div>
</li>

Здесь мы создаем упорядоченный список часов в дне, настраивая класс CSS в зависимости от того, доступен этот слот или нет. Мы прилагаем обработчик кликов, который вызывает метод selectTime()

Наконец, нам нужно реализовать метод selectTime()

 
selectTime : function( hour ) {
    if ( hour.available ) {
        this.time = moment( this.day ).hours( hour.h ).minutes( 0 ).seconds( 0 );      
    }
}

Все, что мы делаем здесь, это клонирование текущего дня и установка времени. Настройка, которая «активирует» последний шаг, показывая форму планирования.

Оформление заказа

Сейчас мы находимся в точке, где у нас есть определенный временной интервал, поэтому пришло время осуществить процесс бронирования.

Учащийся должен будет предоставить некоторую личную информацию. По умолчанию обязательны следующие три поля:

  1. Имя ученика
  2. Фамилия учащегося
  3. Их адрес электронной почты

В сочетании с выбранными датой и временем этого будет достаточно, чтобы запланировать урок.

При желании вы также можете набрать номер телефона, который вы можете установить как «обязательный» на панели управления Acuity.

Acuity также предоставляет возможность создавать настраиваемые поля. Например, вы можете захотеть создать поле для сбора места получения или выгрузки для урока, чтобы указать, является ли это первым уроком клиента, или принять ваши условия. Мы не будем этого делать, чтобы все было относительно просто, но вы можете узнать больше в соответствующем разделе документации .

На скриншоте ниже показано, как будет выглядеть форма бронирования:

Форма заказа

Давайте создадим HTML:

 
<li class="col-md-4" v-bind:class="{ 'active' : time }">
	<header>Confirm Booking</header>
	<div v-if="time" class="book">            
		<header class="well">
			<p>You've selected:</p>
			<h4>{{ time | formatDateTime 'HH:mm' }}</h4>
			<h5>{{ time | formatDateTime 'ddd Do MMM YYYY' }}</h5>
		</header>
		<form v-form name="bookingForm" v-on:submit.prevent="confirm" v-if="bookingStatus == 'pending'">
			<fieldset>
				<div class="alert alert-danger" v-if="bookingForm.$submitted && !bookingForm.$valid">
					<p v-if="bookingForm.firstName.$error.required">First name is required.</p>
					<p v-if="bookingForm.lastName.$error.required">Last name is required.</p>
					<p v-if="bookingForm.email.$error.required">Email is required.</p>
					<p v-if="bookingForm.email.$error.email">Email is not valid.</p>
				</div>
				<div class="form-group">
					<input type="text" name="firstName" placeholder="First name" class="form-control" v-model="learner.firstName" v-form-ctrl required>
				</div>
				<div class="form-group">
					<input type="text" name="lastName" placeholder="Last name" class="form-control" v-model="learner.lastName" v-form-ctrl required>
				</div>
				<div class="form-group">
					<input type="email" name="email" placeholder="E-mail" class="form-control" v-model="learner.email" v-form-ctrl required>
				</div>
				<div class="form-group">
					<button type="submit" class="btn btn-lg btn-primary">Book Now</button>
				</div>      
			</fieldset>
		</form>
		<div class="loading" v-if="bookingStatus == 'sending'">
			<p><img src="/assets/ajax-loader.gif"></p>                  
			<p>Making booking...</p>
		</div>
		<div class="confirmed" v-if="bookingStatus == 'confirmed'">
			<p>Your booking is confirmed. Your reference is:</p>
			<h3>{{ booking.id }}</h3>
			<footer>
				<button class="btn btn-lg btn-primary" v-on:click="reset">Start Over</button>
			</footer>
		</div>
	</div>
</li>

Это может выглядеть немного скучно, но это относительно просто. По сути, это сопоставление элементов управления формы с соответствующими свойствами свойства learner

Большая часть разметки отвечает за проверку. В этом примере я использую Vue Form для проверки формы. Я не буду раскрывать детали того, как это было сделано, но вы можете обратиться к коду или документации для получения дополнительной информации.

<div>confirmed

Вот код для бронирования:

 
confirm : function() {
    if ( this.bookingForm.$valid ) {
        this.bookingStatus = 'sending';

        var data = this.learner;
        data.datetime = this.time.format();

        this.$http.post( 'appointments', data ).then( function( response ) {
            this.booking = response.data;
            this.bookingStatus = 'confirmed';
        });
    }
}

Довольно просто; мы создаем хэш данных, который объединяет информацию нашего учащегося с выбранными датой и временем, а затем размещаем их в нашем бэкэнде. Мы используем свойство bookingStatus Когда свойство бронирования установлено, будет отображаться раздел подтверждения с идентификатором бронирования.

Для справки вот код PHP для назначения:

 
/**
 * Acts as a proxy to `/appointments`
 * We can use this to actually make an appointment
 */
$app->post( 'api/appointments', function( Request $request ) use ( $app ) {

    // Build the data by decoding the JSON and then injecting the `appointmentTypeID`.
    $data = json_decode( $request->getContent(), true ) + [ 'appointmentTypeID' => $app[ 'appointmentTypeID' ] ];   

    // Make the request...
    $response = $app[ 'client' ]->request( 
        '/appointments',
        [
            'method'    =>  'POST',
            'data'      =>  $data,
        ]
    );    

    // If there's an error, write it to the log
    if ( $response[ 'status_code' ] !== 200 ) {
        $app[ 'logger' ]->error( $response[ 'message' ] );
    }
  
    // ...and return it
    return json_encode( $response );

});

Как вы можете видеть, здесь мы в основном комбинируем данные из нашего виджета с идентификатором типа встречи и выполняем аутентифицированный вызов Acuity API.

Итак, вот как будет выглядеть подтверждение:

Подтверждение бронирования

Вот и все, мы сделали!

Что дальше?

Итак, теперь у вас есть виджет бронирования, использующий Acuity Scheduling для управления бронированием и расписанием. Что еще можно с этим сделать? Давайте кратко рассмотрим пару дополнительных (и совершенно необязательных) функций, которые могут еще больше расширить возможности как для ученика, так и для инструктора.

Прием платежей

Мы разрешаем кому-либо запланировать урок при условии, что они будут платить за него в тот же день, но Acuity также предлагает возможность сделать предоплату онлайн. Проверьте документацию для получения дополнительной информации.

Сторонние Интеграции

Существует несколько вариантов интеграции с другими службами, будь то для целей бухгалтерского учета (Quickbooks, Freshbooks), CRM (Salesforce, Zoho) или массовой рассылки (AWeber, Mailchimp). Он также прекрасно интегрируется с Zapier, открывая огромный спектр возможностей. Вы также можете работать с существующим решением календаря (Календарь Google, Outlook) или просто импортировать или экспортировать, используя формат файла .ics

Webhooks

Acuity также предоставляет веб-хуки , так что вы можете создавать свои собственные пользовательские действия, когда встречи запланированы, отменены или переназначены.

Округления

Мы многое изучили, но в ходе этого урока мы не только модернизировали веб-сайт нашего инструктора по вождению, но и потенциально изменили способ управления бронированием. Все побеждают!

Хотите узнать больше? Узнайте, как построить систему бронирования кулинарных классов с помощью Acuity Scheduling, PHP и Lumen.