Интеграция виджета обратного звонка МТС Exolve в документацию на MkDocs

от автора

Привет, Хабр! Это Екатерина Саяпина, Product Owner платформы МТС Exolve. Сегодня покажу, как быстро добавить виджет обратного звонка на страницу, созданную с помощью MkDocs — статического генератора сайтов с уклоном в техническую документацию. Такое размещение виджета бывает нужно в справочных разделах сложных продуктов, где клиентам может потребоваться консультация или разъяснение каких-то технических моментов.

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

Настройка MkDocs через Docker

Начнем с базовой установки MkDocs. Docker используется, чтобы упростить работу с зависимостями. В этом примере мы возьмем готовый образ squidfunk/mkdocs-material. Он содержит не только сам MkDocs, но и популярную тему Material, которая делает документацию красивой и удобной.

Одна команда — и вы получаете полностью настроенный проект с современным дизайном. Не нужно устанавливать Python, pip, возиться с виртуальным окружением — просто запустил контейнер, и все готово к работе:

mkdir cloudy-docs cd cloudy-docs docker run --rm -it -v ${PWD}:/docs squidfunk/mkdocs-material new .

После выполнения этой команды в текущей директории появится структура проекта MkDocs. Затем мы редактируем mkdocs.yml — это главный конфигурационный файл нашего проекта:

site_name: Cloudy Cloud Docs theme:   name: material   palette:     primary: blue  extra_javascript:   - javascripts/exolve-callback.js

Тут мы задаем название сайта, выбираем тему Material и настраиваем синий цвет в качестве основного. Также мы добавляем ссылку на JavaScript-файл нашего виджета обратного звонка.

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

Создаем простую страницу документации

В этом примере мы создаем раздел на сайте с описанием S3 API, где можно найти информацию об основных операциях API для хранения данных в облаке. Использование Markdown упрощает работу с документацией — вы пишете текст с минимальной разметкой, а MkDocs превращает его в красивую HTML-страницу.

Содержимое нашей страницы для примера — в блоке ниже:

# Cloudy Cloud S3 API  Простой и надежный сервис для хранения данных в облаке.  ## Аутентификация  Для доступа к API используйте ваши ключи доступа:  curl -X GET https://s3.cloudy.cloud/buckets \   -H "Authorization: Bearer YOUR_API_KEY"  ## Основные операции  ### Создание бакета curl -X POST https://s3.cloudy.cloud/buckets \   -H "Authorization: Bearer YOUR_API_KEY" \   -H "Content-Type: application/json" \   -d '{"name": "my-bucket", "region": "europe-west"}'  ### Загрузка файла curl -X PUT https://s3.cloudy.cloud/buckets/my-bucket/file.txt \   -H "Authorization: Bearer YOUR_API_KEY" \   -H "Content-Type: text/plain" \   --data-binary @file.txt  ### Скачивание файла curl -X GET https://s3.cloudy.cloud/buckets/my-bucket/file.txt \   -H "Authorization: Bearer YOUR_API_KEY"    ## Нужна помощь? <div id="exolve-callback-container">     Возникли вопросы по API? Наши инженеры готовы помочь!     <div id="exolve-callback-button"></div> </div>

Посмотрите на последний раздел «Нужна помощь?». Здесь будет наша форма для обратного звонка — для этого мы и создаем контейнер с идентификатором exolve-callback-container.

Добавляем виджет

Создадим директорию для JavaScript-файлов и добавим скрипт для виджета:

mkdir -p docs/javascripts cd docs/javascripts touch exolve-callback.js

JavaScript-файл exolve-callback.js — это сердце нашего виджета. Он отвечает за создание кнопки, открытие модального окна при клике на нее, обработку формы и отправку данных в сервис МТС Exolve.

А теперь перейдем к созданию виджета. Код начинается с ожидания загрузки DOM. Затем ищем контейнер для кнопки обратного звонка:

document.addEventListener('DOMContentLoaded', function() { const callbackButton = document.getElementById('exolve-callback-button'); if (callbackButton) {     const button = document.createElement('button');     button.textContent = 'Получить консультацию';     button.className = 'exolve-button';     button.style.backgroundColor = '#2196f3';     button.style.color = '#ffffff';     button.style.padding = '8px 16px';     button.style.border = 'none';     button.style.borderRadius = '4px';     button.style.cursor = 'pointer';     button.style.marginTop = '10px';      button.addEventListener('click', function() {         createModal();     });      callbackButton.appendChild(button); }

Здесь создается затемненный фон (overlay) на весь экран, само модальное окно, его заголовок и форма.

Модальное окно размещается по центру с белым фоном и тенью:

function createModal() {     const overlay = document.createElement('div');     overlay.style.position = 'fixed';     overlay.style.top = '0';     overlay.style.left = '0';     overlay.style.width = '100%';     overlay.style.height = '100%';     overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';     overlay.style.zIndex = '1000';     overlay.style.display = 'flex';     overlay.style.justifyContent = 'center';     overlay.style.alignItems = 'center';      const modal = document.createElement('div');     modal.style.backgroundColor = '#fff';     modal.style.padding = '20px';     modal.style.borderRadius = '8px';     modal.style.maxWidth = '400px';     modal.style.width = '100%';     modal.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.2)';      const title = document.createElement('h3');     title.textContent = 'Запрос обратного звонка';     title.style.marginTop = '0';     title.style.color = '#333';      const form = document.createElement('form');

Следующий блок создает поля формы для ввода имени и телефона. Для каждого поля создается подпись (label) и само окно ввода (input) со стилями. Оба поля помечены как обязательные и заполняют всю ширину родительского элемента:

 const nameLabel = document.createElement('label');     nameLabel.textContent = 'Ваше имя:';     nameLabel.style.display = 'block';     nameLabel.style.marginBottom = '5px';     nameLabel.style.fontWeight = 'bold';      const nameInput = document.createElement('input');     nameInput.type = 'text';     nameInput.required = true;     nameInput.style.width = '100%';     nameInput.style.padding = '8px';     nameInput.style.marginBottom = '15px';     nameInput.style.boxSizing = 'border-box';     nameInput.style.border = '1px solid #ddd';     nameInput.style.borderRadius = '4px';      const phoneLabel = document.createElement('label');     phoneLabel.textContent = 'Ваш номер телефона:';     phoneLabel.style.display = 'block';     phoneLabel.style.marginBottom = '5px';     phoneLabel.style.fontWeight = 'bold';      const phoneInput = document.createElement('input');     phoneInput.type = 'tel';     phoneInput.placeholder = '+7 (___) ___-__-__';     phoneInput.required = true;     phoneInput.style.width = '100%';     phoneInput.style.padding = '8px';     phoneInput.style.marginBottom = '15px';     phoneInput.style.boxSizing = 'border-box';     phoneInput.style.border = '1px solid #ddd';     phoneInput.style.borderRadius = '4px';

Далее создаются кнопки «отмена» и «отправить», которые размещаются в контейнере с гибкой разметкой. Затем вся структура собирается воедино: поля добавляются в форму, кнопки в контейнер, форма и заголовок в модальное окно, а оно в оверлей. Результат добавляется на страницу:

const buttonContainer = document.createElement('div');     buttonContainer.style.display = 'flex';     buttonContainer.style.justifyContent = 'space-between';     buttonContainer.style.marginTop = '20px';      const cancelButton = document.createElement('button');     cancelButton.textContent = 'Отмена';     cancelButton.type = 'button';     cancelButton.style.padding = '8px 16px';     cancelButton.style.backgroundColor = '#f5f5f5';     cancelButton.style.border = '1px solid #ddd';     cancelButton.style.borderRadius = '4px';     cancelButton.style.cursor = 'pointer';      const submitButton = document.createElement('button');     submitButton.textContent = 'Отправить';     submitButton.type = 'submit';     submitButton.style.padding = '8px 16px';     submitButton.style.backgroundColor = '#2196f3';     submitButton.style.color = '#fff';     submitButton.style.border = 'none';     submitButton.style.borderRadius = '4px';     submitButton.style.cursor = 'pointer';      form.appendChild(nameLabel);     form.appendChild(nameInput);     form.appendChild(phoneLabel);     form.appendChild(phoneInput);     buttonContainer.appendChild(cancelButton);     buttonContainer.appendChild(submitButton);     form.appendChild(buttonContainer);     modal.appendChild(title);     modal.appendChild(form);     overlay.appendChild(modal);     document.body.appendChild(overlay);

Теперь время добавить обработчики событий: отправки формы и закрытия модального окна при нажатии на кнопку «Отмена» или на затемненную область. При отправке проверяется заполнение полей, и если все в порядке, данные отправляются в сервис Exolve и показывается сообщение об успехе:

cancelButton.addEventListener('click', function() {         document.body.removeChild(overlay);     });      overlay.addEventListener('click', function(event) {         if (event.target === overlay) {             document.body.removeChild(overlay);         }     });      form.addEventListener('submit', function(event) {         event.preventDefault();         const name = nameInput.value.trim();         const phone = phoneInput.value.trim();         if (!name || !phone) {             alert('Пожалуйста, заполните все поля');             return;         }         sendDataToExolve(name, phone);         document.body.removeChild(overlay);         showSuccessMessage();     }); }

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

function sendDataToExolve(name, phone) {     const cleanPhone = phone.replace(/\D/g, '');     if (typeof window.ExolveCallbackInit !== 'function') {         const script = document.createElement('script');         script.src = 'https://widget.exolve.ru/callback/v1/js/button-loader.min.js';         script.async = true;         document.head.appendChild(script);         script.onload = function() {             makeExolveRequest(name, cleanPhone);         };     } else {         makeExolveRequest(name, cleanPhone);     } }

Структура данных для API в текущей версии просто выводится в консоль с помощью функции makeExolveRequest. В реальном проекте здесь должен быть код отправки запроса на сервер:

function makeExolveRequest(name, phone) {     const requestData = {         callback_resource_id: 1234, // Замените на ваш ID из личного кабинета         number_code: phone,         client_name: name,         line_1: {             destinations: [                 { number: "74951234567" } // Замените на номер вашего оператора             ]         },         line_2: {             destinations: [                 { number: phone }             ]         }     };     console.log('Отправляем данные в Exolve:', requestData);     console.log('Запрос на обратный звонок отправлен'); }

Последняя функция — showSuccessMessage() — создает и показывает уведомление об успешной отправке запроса. Оно появляется в правом нижнем углу экрана как зеленый блок с текстом и автоматически исчезает через 5 секунд:

function showSuccessMessage() {     const messageDiv = document.createElement('div');     messageDiv.style.position = 'fixed';     messageDiv.style.bottom = '20px';     messageDiv.style.right = '20px';     messageDiv.style.backgroundColor = '#4CAF50';     messageDiv.style.color = 'white';     messageDiv.style.padding = '16px';     messageDiv.style.borderRadius = '4px';     messageDiv.style.zIndex = '1000';     messageDiv.textContent = 'Спасибо! Наш специалист скоро свяжется с вами.';     document.body.appendChild(messageDiv);     setTimeout(function() {         document.body.removeChild(messageDiv);     }, 5000); }

Проверка работы

Когда все готово, мы стартуем локальный сервер для разработки. Опять же используем Docker, чтобы не устанавливать ничего на компьютер. Команда запускает контейнер с MkDocs и открывает порт 8 000 для доступа к сайту.

docker run --rm -it -p 8000:8000 -v ${PWD}:/docs squidfunk/mkdocs-material

Теперь мы можем открыть http://localhost:8000 в браузере и увидеть нашу страницу с виджетом:

Так можно все проверить перед публикацией на сайт.

Как работает виджет?

Когда пользователь нажимает на кнопку, виджет:

  1. Пользователь нажимает на кнопку «Получить консультацию».

  2. Открывается модальное окно с формой для ввода имени и номера телефона.

  3. После заполнения формы и нажатия «Отправить» данные передаются в МТС Exolve.

  4. Пользователь получает уведомление об успешной обработке запроса.

  5. Сервис МТС Exolve инициирует звонок оператору и пользователю и соединяет их.

На серверной стороне это обрабатывается с помощью API МТС Exolve, которое принимает запрос в формате json:

{     "callback_resource_id": 1234,     "number_code": "79991234567",     "client_name": "Иван Петров",     "line_1": {         "destinations": [             { "number": "74951234567" }         ]     },     "line_2": {         "destinations": [             { "number": "79991234567" }         ]     } }

Финальная сборка

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

docker run --rm -it -v ${PWD}:/docs squidfunk/mkdocs-material build

Команда build создает полностью статический сайт, который можно разместить на любом хостинге. Готовые файлы появляются в директории site/. Это удобно, так как вам не нужно устанавливать никакое серверное программное обеспечение — просто загрузите файлы, и все готово.

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

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


ссылка на оригинал статьи https://habr.com/ru/articles/900676/