multy-stub/server/routers/questioneer/public/static/js/create.js
Primakov Alexandr Alexandrovich dd589790c2 fix путей
2025-03-12 00:29:55 +03:00

368 lines
14 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
const getApiPath = () => {
// Проверяем, содержит ли путь /ms/ (продакшн на dev.bro-js.ru)
const pathname = window.location.pathname;
const isMsPath = pathname.includes('/ms/questioneer');
if (isMsPath) {
// Для продакшна: если в пути есть /ms/, то API доступно по /ms/questioneer/api
return '/ms/questioneer/api';
} else {
// Для локальной разработки: используем обычный путь
// Извлекаем базовый путь из URL страницы
const pathParts = pathname.split('/');
// Если последний сегмент пустой (из-за /) - удаляем его
if (pathParts[pathParts.length - 1] === '') {
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 isMsPath = window.location.pathname.includes('/ms/questioneer');
let basePath;
if (isMsPath) {
// Для продакшна: используем /ms/questioneer
basePath = '/ms/questioneer';
} else {
// Для локальной разработки: используем текущий путь
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();
});