init Questionnaire
This commit is contained in:
332
server/routers/questioneer/public/static/js/edit.js
Normal file
332
server/routers/questioneer/public/static/js/edit.js
Normal file
@@ -0,0 +1,332 @@
|
||||
/* global $, window, document, showAlert, showConfirm, showQRCodeModal */
|
||||
$(document).ready(function() {
|
||||
const form = $('#edit-questionnaire-form');
|
||||
const questionsList = $('#questions-list');
|
||||
const addQuestionBtn = $('#add-question');
|
||||
const adminLink = window.location.pathname.split('/').pop();
|
||||
|
||||
let questionCount = 0;
|
||||
let questionnaireData = null;
|
||||
|
||||
// Получаем базовый путь API
|
||||
const getApiPath = () => {
|
||||
const pathParts = window.location.pathname.split('/');
|
||||
// Убираем последние две части пути (edit/:adminLink)
|
||||
pathParts.pop();
|
||||
pathParts.pop();
|
||||
return pathParts.join('/') + '/api';
|
||||
};
|
||||
|
||||
// Загрузка данных опроса
|
||||
const loadQuestionnaire = () => {
|
||||
$.ajax({
|
||||
url: `${getApiPath()}/questionnaires/admin/${adminLink}`,
|
||||
method: 'GET',
|
||||
success: function(result) {
|
||||
if (result.success) {
|
||||
questionnaireData = result.data;
|
||||
fillFormData();
|
||||
$('#loading').hide();
|
||||
$('#edit-form-container').show();
|
||||
} else {
|
||||
$('#loading').text(`Ошибка: ${result.error}`);
|
||||
}
|
||||
},
|
||||
error: function(error) {
|
||||
console.error('Error loading questionnaire:', error);
|
||||
$('#loading').text('Не удалось загрузить опрос. Пожалуйста, попробуйте позже.');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Заполнение формы данными опроса
|
||||
const fillFormData = () => {
|
||||
// Заполняем основные данные
|
||||
$('#title').val(questionnaireData.title);
|
||||
$('#description').val(questionnaireData.description || '');
|
||||
$('#display-type').val(questionnaireData.displayType);
|
||||
|
||||
// Формируем ссылки
|
||||
const baseUrl = window.location.origin;
|
||||
const baseQuestionnairePath = window.location.pathname.split('/edit')[0];
|
||||
const publicUrl = `${baseUrl}${baseQuestionnairePath}/poll/${questionnaireData.publicLink}`;
|
||||
const adminUrl = `${baseUrl}${baseQuestionnairePath}/admin/${questionnaireData.adminLink}`;
|
||||
|
||||
$('#public-link').val(publicUrl);
|
||||
$('#admin-link').val(adminUrl);
|
||||
|
||||
// Добавляем вопросы
|
||||
questionsList.empty();
|
||||
|
||||
if (questionnaireData.questions && questionnaireData.questions.length > 0) {
|
||||
questionnaireData.questions.forEach((question, index) => {
|
||||
addQuestion(question);
|
||||
});
|
||||
} else {
|
||||
// Если нет вопросов, добавляем пустой
|
||||
addQuestion();
|
||||
}
|
||||
|
||||
renumberQuestions();
|
||||
};
|
||||
|
||||
// Добавление нового вопроса
|
||||
addQuestionBtn.on('click', function() {
|
||||
addQuestion();
|
||||
renumberQuestions();
|
||||
});
|
||||
|
||||
// Обработка отправки формы
|
||||
form.on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
saveQuestionnaire();
|
||||
});
|
||||
|
||||
// Делегирование событий для динамических элементов
|
||||
questionsList.on('click', '.delete-question', function() {
|
||||
// Удаление вопроса
|
||||
const questionItem = $(this).closest('.question-item');
|
||||
questionItem.remove();
|
||||
renumberQuestions();
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
// Делегирование для изменения типа вопроса
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
// Копирование ссылок
|
||||
$('#copy-public-link').on('click', function() {
|
||||
$('#public-link').select();
|
||||
document.execCommand('copy');
|
||||
showAlert('Ссылка для голосования скопирована в буфер обмена', 'Копирование');
|
||||
});
|
||||
|
||||
$('#copy-admin-link').on('click', function() {
|
||||
$('#admin-link').select();
|
||||
document.execCommand('copy');
|
||||
showAlert('Административная ссылка скопирована в буфер обмена', 'Копирование');
|
||||
});
|
||||
|
||||
// Отображение QR-кода
|
||||
$('#show-qr-code').on('click', function() {
|
||||
const publicUrl = $('#public-link').val();
|
||||
showQRCodeModal(publicUrl, 'QR-код для голосования');
|
||||
});
|
||||
|
||||
// Возврат к админке
|
||||
$('#back-to-admin').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
const basePath = window.location.pathname.split('/edit')[0];
|
||||
window.location.href = `${basePath}/admin/${adminLink}`;
|
||||
});
|
||||
|
||||
// Функция для добавления нового вопроса
|
||||
function addQuestion(questionData) {
|
||||
const template = $('#question-template').html();
|
||||
const index = questionCount++;
|
||||
|
||||
// Заменяем плейсхолдеры в шаблоне
|
||||
let questionHtml = template
|
||||
.replace(/\{\{index\}\}/g, index)
|
||||
.replace(/\{\{number\}\}/g, index + 1);
|
||||
|
||||
questionsList.append(questionHtml);
|
||||
|
||||
// Если есть данные вопроса - заполняем поля
|
||||
if (questionData) {
|
||||
$(`#question-text-${index}`).val(questionData.text);
|
||||
$(`#question-type-${index}`).val(questionData.type);
|
||||
|
||||
if (questionData.required) {
|
||||
$(`input[name="questions[${index}][required]"]`).prop('checked', true);
|
||||
}
|
||||
|
||||
// Добавляем варианты ответа если они есть
|
||||
if (questionData.options && questionData.options.length > 0) {
|
||||
questionData.options.forEach(option => {
|
||||
addOption(index, option.text);
|
||||
});
|
||||
}
|
||||
|
||||
// Заполняем настройки шкалы если нужно
|
||||
if (questionData.scaleMax) {
|
||||
$(`#scale-max-${index}`).val(questionData.scaleMax);
|
||||
}
|
||||
}
|
||||
|
||||
// Показываем/скрываем контейнеры в зависимости от типа вопроса
|
||||
const questionType = $(`#question-type-${index}`).val();
|
||||
if (['single_choice', 'multiple_choice', 'tag_cloud'].includes(questionType)) {
|
||||
$(`#options-container-${index}`).show();
|
||||
$(`#scale-container-${index}`).hide();
|
||||
|
||||
// Если нет вариантов и не загружены данные, добавляем два
|
||||
if (!questionData && $(`#options-list-${index}`).children().length === 0) {
|
||||
addOption(index);
|
||||
addOption(index);
|
||||
}
|
||||
} else if (questionType === 'scale') {
|
||||
$(`#options-container-${index}`).hide();
|
||||
$(`#scale-container-${index}`).show();
|
||||
} else {
|
||||
$(`#options-container-${index}`).hide();
|
||||
$(`#scale-container-${index}`).hide();
|
||||
}
|
||||
}
|
||||
|
||||
// Функция для добавления варианта ответа
|
||||
function addOption(questionIndex, optionText) {
|
||||
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);
|
||||
|
||||
// Если есть текст варианта - устанавливаем его
|
||||
if (optionText) {
|
||||
optionsList.children().last().find('input[type="text"]').val(optionText);
|
||||
}
|
||||
|
||||
// Проверяем, видим ли контейнер опций
|
||||
const optionsContainer = $(`#options-container-${questionIndex}`);
|
||||
if (optionsContainer.is(':hidden')) {
|
||||
// Если контейнер скрыт, отключаем required у полей ввода
|
||||
optionsList.find('input[type="text"]').prop('required', false);
|
||||
}
|
||||
}
|
||||
|
||||
// Перенумерация вопросов
|
||||
function renumberQuestions() {
|
||||
$('.question-item').each(function(index) {
|
||||
$(this).find('h3').text(`Вопрос ${index + 1}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Сохранение опроса
|
||||
function saveQuestionnaire() {
|
||||
const questionnaire = {
|
||||
title: $('#title').val(),
|
||||
description: $('#description').val(),
|
||||
displayType: $('#display-type').val(),
|
||||
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.scaleMax = parseInt($(`#scale-max-${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) {
|
||||
// Сохраняем количество голосов из старых данных
|
||||
let count = 0;
|
||||
const optionIndex = $(this).data('index');
|
||||
|
||||
if (questionnaireData &&
|
||||
questionnaireData.questions[index] &&
|
||||
questionnaireData.questions[index].options &&
|
||||
questionnaireData.questions[index].options[optionIndex]) {
|
||||
count = questionnaireData.questions[index].options[optionIndex].count || 0;
|
||||
}
|
||||
|
||||
question.options.push({
|
||||
text: optionText,
|
||||
count: count
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
questionnaire.questions.push(question);
|
||||
});
|
||||
|
||||
// Отправка на сервер
|
||||
$.ajax({
|
||||
url: `${getApiPath()}/questionnaires/${adminLink}`,
|
||||
method: 'PUT',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(questionnaire),
|
||||
success: function(result) {
|
||||
if (result.success) {
|
||||
showAlert('Опрос успешно обновлен', 'Успешно', function() {
|
||||
const basePath = window.location.pathname.split('/edit')[0];
|
||||
window.location.href = `${basePath}/admin/${adminLink}`;
|
||||
});
|
||||
} else {
|
||||
showAlert(`Ошибка при обновлении опроса: ${result.error}`, 'Ошибка');
|
||||
}
|
||||
},
|
||||
error: function(error) {
|
||||
console.error('Error updating questionnaire:', error);
|
||||
showAlert('Не удалось обновить опрос. Пожалуйста, попробуйте позже.', 'Ошибка');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Инициализация
|
||||
loadQuestionnaire();
|
||||
});
|
||||
Reference in New Issue
Block a user