multy-stub/server/routers/questioneer/public/static/js/create.js
Primakov Alexandr Alexandrovich 1fcc5ed70d init Questionnaire
2025-03-11 23:50:50 +03:00

343 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* global $, window, document, alert, showAlert, showConfirm */
$(document).ready(function() {
const form = $('#create-questionnaire-form');
const questionsList = $('#questions-list');
const addQuestionBtn = $('#add-question');
let questionCount = 0;
// Получаем базовый путь API (для работы и с /questioneer, и с /ms/questioneer)
const getApiPath = () => {
const pathParts = window.location.pathname.split('/');
// Убираем последнюю часть пути (create)
pathParts.pop();
return pathParts.join('/') + '/api';
};
// Добавление нового вопроса
addQuestionBtn.on('click', function() {
addQuestion();
});
// Обработка отправки формы
form.on('submit', function(e) {
e.preventDefault();
saveQuestionnaire();
});
// Делегирование событий для динамических элементов
questionsList.on('click', '.delete-question', function() {
// Удаление вопроса
const questionItem = $(this).closest('.question-item');
showConfirm('Вы уверены, что хотите удалить этот вопрос?', function(confirmed) {
if (confirmed) {
questionItem.remove();
renumberQuestions();
// Вызываем функцию обновления атрибутов required
updateRequiredAttributes();
}
});
});
questionsList.on('click', '.add-option', function() {
// Добавление варианта ответа
const questionIndex = $(this).data('question-index');
addOption(questionIndex);
});
questionsList.on('click', '.delete-option', function() {
// Удаление варианта ответа
$(this).closest('.option-item').remove();
// Вызываем функцию обновления атрибутов required
updateRequiredAttributes();
});
// Делегирование для изменения типа вопроса
questionsList.on('change', '.question-type-select', function() {
const questionItem = $(this).closest('.question-item');
const questionIndex = questionItem.data('index');
const optionsContainer = $(`#options-container-${questionIndex}`);
const scaleContainer = $(`#scale-container-${questionIndex}`);
// Скрыть/показать варианты ответа в зависимости от типа вопроса
const questionType = $(this).val();
if (['single_choice', 'multiple_choice', 'tag_cloud'].includes(questionType)) {
optionsContainer.show();
scaleContainer.hide();
// Если нет вариантов, добавляем два
const optionsList = $(`#options-list-${questionIndex}`);
if (optionsList.children().length === 0) {
addOption(questionIndex);
addOption(questionIndex);
}
// Включаем required для полей ввода вариантов
optionsList.find('input[type="text"]').prop('required', true);
} else if (questionType === 'scale') {
optionsContainer.hide();
scaleContainer.show();
// Отключаем required для скрытых полей
$(`#options-list-${questionIndex}`).find('input[type="text"]').prop('required', false);
} else {
optionsContainer.hide();
scaleContainer.hide();
// Отключаем required для скрытых полей
$(`#options-list-${questionIndex}`).find('input[type="text"]').prop('required', false);
}
// Вызываем функцию обновления атрибутов required
updateRequiredAttributes();
});
// Функция для добавления нового вопроса
function addQuestion() {
const template = $('#question-template').html();
const index = questionCount++;
// Заменяем плейсхолдеры в шаблоне
let questionHtml = template
.replace(/\{\{index\}\}/g, index)
.replace(/\{\{number\}\}/g, index + 1);
questionsList.append(questionHtml);
// Показываем/скрываем контейнер вариантов в зависимости от типа вопроса
const questionType = $(`#question-type-${index}`).val();
if (!['single_choice', 'multiple_choice', 'tag_cloud'].includes(questionType)) {
$(`#options-container-${index}`).hide();
// Отключаем required для скрытых полей
$(`#options-list-${index}`).find('input[type="text"]').prop('required', false);
} else {
// Добавляем пару начальных вариантов ответа
addOption(index);
addOption(index);
}
if (questionType === 'scale') {
$(`#scale-container-${index}`).show();
} else {
$(`#scale-container-${index}`).hide();
}
// Вызываем функцию обновления атрибутов required
updateRequiredAttributes();
}
// Функция для добавления варианта ответа
function addOption(questionIndex) {
const optionsList = $(`#options-list-${questionIndex}`);
const template = $('#option-template').html();
const optionIndex = optionsList.children().length;
// Заменяем плейсхолдеры в шаблоне
let optionHtml = template
.replace(/\{\{questionIndex\}\}/g, questionIndex)
.replace(/\{\{optionIndex\}\}/g, optionIndex);
optionsList.append(optionHtml);
// Проверяем, видим ли контейнер опций
const optionsContainer = $(`#options-container-${questionIndex}`);
if (optionsContainer.is(':hidden')) {
// Если контейнер скрыт, отключаем required у полей ввода
optionsList.find('input[type="text"]').prop('required', false);
}
// Вызываем функцию обновления атрибутов required
updateRequiredAttributes();
}
// Перенумерация вопросов
function renumberQuestions() {
$('.question-item').each(function(index) {
$(this).find('h3').text(`Вопрос ${index + 1}`);
});
}
// Функция для обновления нумерации вопросов
function updateQuestionNumbers() {
$('.question-item').each(function(index) {
$(this).find('h3').text(`Вопрос ${index + 1}`);
});
}
// Сохранение опроса
function saveQuestionnaire() {
const questionnaire = {
title: $('#title').val(),
description: $('#description').val(),
displayType: 'step_by_step', // Всегда устанавливаем пошаговый режим
questions: []
};
// Собираем данные о вопросах
$('.question-item').each(function() {
const index = $(this).data('index');
const questionType = $(`#question-type-${index}`).val();
const question = {
text: $(`#question-text-${index}`).val(),
type: questionType,
required: $(`input[name="questions[${index}][required]"]`).is(':checked'),
options: []
};
// Добавляем настройки шкалы если нужно
if (questionType === 'scale') {
question.scaleMin = parseInt($(`#scale-min-${index}`).val()) || 0;
question.scaleMax = parseInt($(`#scale-max-${index}`).val()) || 10;
question.scaleMinLabel = $(`#scale-min-label-${index}`).val() || 'Минимум';
question.scaleMaxLabel = $(`#scale-max-label-${index}`).val() || 'Максимум';
}
// Собираем варианты ответа если это не текстовый вопрос или шкала
if (['single_choice', 'multiple_choice', 'tag_cloud'].includes(questionType)) {
$(`#options-list-${index} .option-item`).each(function() {
const optionText = $(this).find('input[type="text"]').val();
if (optionText) {
question.options.push({
text: optionText,
count: 0
});
}
});
}
questionnaire.questions.push(question);
});
// Отправка на сервер
$.ajax({
url: `${getApiPath()}/questionnaires`,
method: 'POST',
contentType: 'application/json',
data: JSON.stringify(questionnaire),
success: function(result) {
if (result.success) {
// Перенаправление на страницу администрирования опроса
const basePath = window.location.pathname.split('/create')[0];
window.location.href = `${basePath}/admin/${result.data.adminLink}`;
} else {
showAlert(`Ошибка при создании опроса: ${result.error}`, 'Ошибка');
}
},
error: function(error) {
console.error('Error creating questionnaire:', error);
showAlert('Не удалось создать опрос. Пожалуйста, попробуйте позже.', 'Ошибка');
}
});
}
// Функция для обновления атрибута required в зависимости от видимости полей
function updateRequiredAttributes() {
// Для полей вопросов
$('.question-item').each(function() {
const questionType = $(this).find('.question-type-select').val();
const textInput = $(this).find('.question-text');
const optionsContainer = $(this).find('.options-container');
// Обновляем required для текстового поля вопроса
if (textInput.is(':visible')) {
textInput.prop('required', true);
} else {
textInput.prop('required', false);
}
// Обновляем required для полей опций
if (questionType === 'single_choice' || questionType === 'multiple_choice') {
optionsContainer.find('input[type="text"]').each(function() {
if ($(this).is(':visible')) {
$(this).prop('required', true);
} else {
$(this).prop('required', false);
}
});
} else {
optionsContainer.find('input[type="text"]').prop('required', false);
}
// Для шкалы оценки
if (questionType === 'scale') {
const minInput = $(this).find('.scale-min');
const maxInput = $(this).find('.scale-max');
const minLabelInput = $(this).find('.scale-min-label');
const maxLabelInput = $(this).find('.scale-max-label');
if (minInput.is(':visible')) minInput.prop('required', true);
else minInput.prop('required', false);
if (maxInput.is(':visible')) maxInput.prop('required', true);
else maxInput.prop('required', false);
if (minLabelInput.is(':visible')) minLabelInput.prop('required', true);
else minLabelInput.prop('required', false);
if (maxLabelInput.is(':visible')) maxLabelInput.prop('required', true);
else maxLabelInput.prop('required', false);
}
});
// Для основных полей формы
const titleInput = $('#title');
const descriptionInput = $('#description');
if (titleInput.is(':visible')) titleInput.prop('required', true);
else titleInput.prop('required', false);
if (descriptionInput.is(':visible')) descriptionInput.prop('required', false); // Описание не обязательно
}
// Инициализация с одним вопросом
addQuestion();
// Обработчик отправки формы
$('#create-questionnaire-form').on('submit', function(e) {
// Обновляем атрибуты required перед отправкой
updateRequiredAttributes();
// Проверяем валидность формы
if (!this.checkValidity()) {
e.preventDefault();
e.stopPropagation();
// Находим первый невалидный элемент и прокручиваем к нему
const firstInvalid = $(this).find(':invalid').first();
if (firstInvalid.length) {
$('html, body').animate({
scrollTop: firstInvalid.offset().top - 100
}, 500);
// Добавляем класс ошибки к родительскому элементу вопроса
firstInvalid.closest('.question-item').addClass('error');
setTimeout(() => {
firstInvalid.closest('.question-item').removeClass('error');
}, 3000);
}
}
$(this).addClass('was-validated');
});
// Инициализируем атрибуты required
updateRequiredAttributes();
});
// Обработчик удаления вопроса
$(document).on('click', '.remove-question', function() {
$(this).closest('.question-item').remove();
updateQuestionNumbers();
// Вызываем функцию обновления атрибутов required
updateRequiredAttributes();
});
// Обработчик удаления опции
$(document).on('click', '.remove-option', function() {
$(this).closest('.option-item').remove();
// Вызываем функцию обновления атрибутов required
updateRequiredAttributes();
});