Делаем калькулятор доставка на сайте WordPress

Делаем калькулятор доставка на сайте Wordpress

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

Добавляем tabs с доставкой на странице товара

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

Вставить на сайт форму я решил в дополнительный вкладке доп информации woocommerce./

Так было до. А дальше скрин после добавления вкладки с рассчетом доставки.

Код добавления вкладки следующий

add_filter( 'woocommerce_product_tabs', 'woo_delivery_tab' );
 function woo_delivery_tab( $tabs ) {
     // Только для категорий кухни и шкафы
     global $product;
     if ( has_term( array( 'Кухни', 'Шкафы-купе', 'Прихожие', 'Диваны' ), 'product_cat', $product->get_id() )) {
         $tabs['woo_delivery_tab'] = array(
             'title' 	=> __( 'Доставка', 'woocommerce' ),
             'priority' 	=> 20,
             'callback' 	=> 'woo_delivery_tab_content'
         );
         return $tabs;
     } else {
         return $tabs;
     } 
 }

 function woo_delivery_tab_content() {
    global $product;
    // проверяем, принадлежит ли товар категории "Кухни" 
    if ( has_term( 'Кухни', 'product_cat', $product->get_id()) ) {
        // Подключаем файл с нужным HTML-кодом
        get_template_part( 'template-parts/tabs/delivery-kitchen', 25 );
    }
}

При этом тут есть функция get_template_part( ‘template-parts/tabs/delivery-kitchen’, 25 ); которая подразумевает что у нас в папке дочерней темы по пути template-parts/tabs/ находится файл delivery-kitchen.php в котором мы и будем писать логику.

Пишем код калькулятора на html + js

Никаких данных из самого товара я решил не подтягивать, а потому на php ничего писать не пришлось. Код файла delivery-kitchen.php это html код и js скрипт. Все очень просто. Кстати почему файл так называется, потому что я для каждой категории товаров скорее всего буду делать свой калькулятор, так как вариант просчета доставка для кухни и например дивана будет немного отличаться.

Итак код верстки самой формы такой.

<div class="calculator">
  <div class="delivery-form">
    <div>
      <h2 class="has-text-align-center wp-block-heading" access="false" id="control-1417866">Рассчитать стоимость доставки</h2>
    </div>
    <form>
      <div>
        <label for="size">Размер (пог. метр):</label>
        <input type="number" id="size" name="size" value="3" placeholder="Длина кухни/шкафа в погонных метрах" required>
        <div id="size-error" class="invalid-feedback"></div>
      </div>    
      <div>
        <label for="delivery_city">Куда:</label>
        <select name="delivery_city" id="delivery_city" required>
          <option value="Москва">Москва</option>
          <option value="Санкт-Петербург">Санкт-Петербург</option>
          <option value="Новосибирск">Новосибирск</option>
          <option value="Екатеринбург">Екатеринбург</option>
          <option value="Нижний Новгород">Нижний Новгород</option>
          <option value="Казань">Казань</option>
          <option value="Челябинск">Челябинск</option>
          <option value="Омск">Омск</option>
          <option value="Самара">Самара</option>
          <option value="Ростов-на-Дону">Ростов-на-Дону</option>
          <option value="Уфа">Уфа</option>
          <option value="Красноярск">Красноярск</option>
          <option value="Пермь">Пермь</option>
          <option value="Воронеж">Воронеж</option>
          <option value="Волгоград">Волгоград</option>
        </select>
      </div>
      <div>
        <label for="weight">Вес груза (кг):</label>
        <input type="number" id="weight" name="weight" min="1" max="10000" placeholder="Укажите вес" value="150" required>
        <div id="weight-error" class="invalid-feedback"></div>
      </div>
      <div>
        <label for="volume">Объем груза (м³):</label>
        <input type="number" id="volume" name="volume" min="0.7" step="0.1" placeholder="Укажите объем" required value="2.1">
        <div id="volume-error" class="invalid-feedback"></div>
      </div>
    </form>
  </div>

  <div class="result-column">
    <div class="result-wrapper">
      <h2 class="has-text-align-center wp-block-heading">Стоимость доставки</h2>
      <p id="total_cost" class="result-value">0 руб.</p>
      <div>
        <button class="calculate-btn" type="button" onclick="calculateDelivery()">Рассчитать</button>
      </div>      
    </div>
  </div>
</div>

Тут ничего особенного 2 колонки flex блоков. В первой элементы ввода во второй расчет стоимости. Стоит отметить что код самой формы почти на 80% написан с использованием chatGPT.

Далее привожу часть файла в которой располагается js код

<script>
const citySelect = document.getElementById("delivery_city");
const weightInput = document.getElementById("weight");
const volumeInput = document.getElementById("volume");
const sizeInput = document.getElementById("size");
const totalCostDiv = document.getElementById("total_cost");

const sizeError = document.getElementById("size-error");
const weightError = document.getElementById("weight-error");
const volumeError = document.getElementById("volume-error");

function calculateDelivery() {
  document.getElementById("total_cost").innerHTML = "";

  const city = citySelect.value;
  const weight = weightInput.value;
  const volume = volumeInput.value;
  const size = parseFloat(sizeInput.value);;

  let cost = 0;

  switch (city) {
    case "Москва":
      cost = 500;
      break;
    case "Санкт-Петербург":
      cost = 600;
      break;
    case "Новосибирск":
      cost = 1000;
      break;
    case "Екатеринбург":
      cost = 900;
      break;
    case "Нижний Новгород":
      cost = 700;
      break;
    case "Казань":
      cost = 800;
      break;
    case "Челябинск":
      cost = 1000;
      break;
    case "Омск":
      cost = 1200;
      break;
    case "Самара":
      cost = 900;
      break;
    case "Ростов-на-Дону":
      cost = 1100;
      break;
    case "Уфа":
      cost = 1100;
      break;
    case "Красноярск":
      cost = 1300;
      break;
    case "Пермь":
      cost = 1000;
      break;
    case "Воронеж":
      cost = 800;
      break;
    case "Волгоград":
      cost = 1200;
      break;
    default:
      break;
  }

  if (isNaN(weight) || weight < 50) {
    weightError.textContent = 'Введите корректное значение веса';
    weightInput.classList.add('is-invalid');
  } else {
    weightError.textContent = '';
    weightInput.classList.remove('is-invalid');
    // Округляем вес до ближайшего числа, оканчивающегося на 50
    let roundedWeight = Math.ceil(weight/50) * 50;
    // Вычисляем коэффициент увеличения стоимости
    let coefficient = roundedWeight/50 + 1;
    // Умножаем стоимость доставки на коэффициент
    cost *= coefficient;
 
  }

  if (isNaN(size) || size < 1) {
    sizeError.textContent = 'Введите корректное значение размера';
    sizeInput.classList.add('is-invalid');
  } else {
    sizeError.textContent = '';
    sizeInput.classList.remove('is-invalid');
    cost = Math.ceil(cost *size);  
  }

  if (isNaN(volume) || volume < 0.7) {
    volumeError.textContent = 'Введите корректное значение объема';
    volumeInput.classList.add('is-invalid');
  } else {
    volumeError.textContent = '';
    volumeInput.classList.remove('is-invalid');
    cost += (volume/0.7)*1036;
 
  }

  totalCostDiv.innerHTML = `${cost} руб.`;
}

sizeInput.addEventListener('input', function() {
  totalCostDiv.innerHTML = '';
});

sizeInput.addEventListener('change', () => {
  const sizeValue = parseFloat(sizeInput.value);
  if (!isNaN(sizeValue)) {
    //Изменяем вес с шагом 50
    const weightValue = sizeValue * 50;
    weightInput.value = weightValue;
    //Изменяем объем с шагом 0.7
    const volumeValue = (sizeValue * 0.7).toFixed(1);
    volumeInput.value = volumeValue;
  }
});

</script>

В целом по коду думаю итак все понятно. По id элементов формы мы получаем значения полей и в функции calculateDelivery() делаем свой рассчет. Также есть простая валидация на заполнение элементов ввода. Размера, объема и веса. Отмечу что я свою форму заполняю начальными данным, например размер 3 метра, вест 150 кг. объем 2.1 куба.
И при изменении размера также изменяю параметры груза с определнным шагом, для веса 50 кг, для объема 0.7 кубов. Цифры для себя я взял эмпирические у вас они могут быть другими.

Ну и конечно же добавим стили css для адекватного отображения формы.

<style>

.is-invalid {
  border-color: #dc3545; /* устанавливаем цвет рамки элемента */
  padding-right: calc(1.5em + .75rem); /* увеличиваем правый отступ, чтобы было место для иконки ошибки */
  background-image: url('path/to/error-icon.png'); /* устанавливаем изображение иконки ошибки */
  background-repeat: no-repeat; /* запрещаем повторение изображения */
  background-position: right calc(.375em + .1875rem) center; /* устанавливаем позицию иконки ошибки */
  background-size: calc(.75em + .375rem) calc(.75em + .375rem); /* устанавливаем размер иконки ошибки */
}

.is-invalid:focus {
  border-color: #dc3545; /* устанавливаем цвет рамки при фокусе */
  box-shadow: 0 0 0 .2rem rgba(220,53,69,.25); /* добавляем тень при фокусе */
}

.is-invalid ~ .invalid-feedback {
  display: block; /* показываем блок с сообщением об ошибке */
}


.invalid-feedback {
  color: red;
  font-size: 14px;
  margin-top: 5px;
}  

.calculator {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.result-column {
  flex-basis: 33.33%; /* одна треть ширины */
  flex-grow: 1;
}
.result-wrapper {
  background-color: #f5f5f5;
  padding: 50px 10px;
  border-radius: 5px;
  text-align: center;
}

.result-text {
  margin-bottom: 10px;
  font-size: 16px;
  font-weight: bold;
}

.result-value {
  font-size: 30px;
  font-weight: bold;
}

.delivery-form {
  display: flex;
  flex-direction: column;
  flex-basis: 66.67%; /* две трети ширины */
  flex-grow: 1;
  padding-right: 20px;    
  gap: 10px;  
  font-family: Arial, sans-serif;
}


.delivery-form label {
  display: block;
  margin-bottom: 5px;
  font-weight: bold;
}

.delivery-form select,
.delivery-form input[type="number"],
.delivery-form input[type="checkbox"] {
  width: 100%;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 4px;
  box-sizing: border-box;
  margin-bottom: 10px;
}

.delivery-form input[type="checkbox"] {
  display: inline-block;
  margin-right: 10px;
}

.calculate-btn {
  padding: 10px 20px;
  border-radius: 5px;
  border: none;
  background-color: rgb(26, 137, 23); /* зеленый цвет */
  color: #fff;
  cursor: pointer;
  transition: all 0.3s ease;
  align-self: flex-start;
}

.calculate-btn:hover {
  background-color: #136f13;
}

.delivery-form #total_cost {
  margin-top: 10px;
  font-weight: bold;
}

</style> 

Итог

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

Вам также может понравиться

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Яндекс.Метрика