first commit 2
This commit is contained in:
@@ -0,0 +1,394 @@
|
||||
/**
|
||||
* 설문 작성/수정 폼 JavaScript
|
||||
*/
|
||||
|
||||
let questionCount = 0;
|
||||
const questionTypes = {
|
||||
'text': '단답형',
|
||||
'textarea': '장문형',
|
||||
'radio': '단일선택',
|
||||
'checkbox': '다중선택',
|
||||
'select': '드롭다운',
|
||||
'rating': '평점',
|
||||
'date': '날짜'
|
||||
};
|
||||
|
||||
// 페이지 로드 시 초기화
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
initSurveyForm();
|
||||
});
|
||||
|
||||
function initSurveyForm() {
|
||||
// 탭 전환 이벤트
|
||||
initTabs();
|
||||
|
||||
// 템플릿 선택 이벤트
|
||||
initTemplateSelection();
|
||||
|
||||
// 폼 검증 이벤트
|
||||
initFormValidation();
|
||||
|
||||
// 기존 질문들의 타입에 따라 옵션 표시
|
||||
document.querySelectorAll('.question-type-select').forEach(select => {
|
||||
updateQuestionType(select);
|
||||
});
|
||||
|
||||
// URL에서 template_id가 있으면 해당 템플릿 자동 선택
|
||||
const templateId = document.getElementById('templateId')?.value;
|
||||
if (templateId > 0) {
|
||||
const templateCard = document.querySelector(`[data-template="${templateId}"]`);
|
||||
if (templateCard) {
|
||||
templateCard.click();
|
||||
}
|
||||
}
|
||||
|
||||
// 질문 개수 초기화
|
||||
questionCount = document.querySelectorAll('.question-item').length;
|
||||
}
|
||||
|
||||
// 탭 전환 기능
|
||||
function initTabs() {
|
||||
document.querySelectorAll('.form-tab').forEach(tab => {
|
||||
tab.addEventListener('click', function() {
|
||||
const targetTab = this.dataset.tab;
|
||||
|
||||
// 탭 활성화
|
||||
document.querySelectorAll('.form-tab').forEach(t => t.classList.remove('active'));
|
||||
this.classList.add('active');
|
||||
|
||||
// 콘텐츠 표시
|
||||
document.querySelectorAll('.tab-content').forEach(content => {
|
||||
content.classList.remove('active');
|
||||
});
|
||||
document.getElementById(targetTab).classList.add('active');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 템플릿 선택 기능
|
||||
function initTemplateSelection() {
|
||||
document.querySelectorAll('.template-card').forEach(card => {
|
||||
card.addEventListener('click', function() {
|
||||
document.querySelectorAll('.template-card').forEach(c => c.classList.remove('selected'));
|
||||
this.classList.add('selected');
|
||||
|
||||
const templateId = this.dataset.template;
|
||||
document.getElementById('templateId').value = templateId;
|
||||
|
||||
// 템플릿 질문 로드
|
||||
if (templateId > 0) {
|
||||
loadTemplateQuestions(templateId);
|
||||
} else {
|
||||
// 직접 작성 선택 시 기존 질문들 초기화
|
||||
document.getElementById('questionsList').innerHTML = '';
|
||||
questionCount = 0;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 템플릿 질문 로드
|
||||
function loadTemplateQuestions(templateId) {
|
||||
fetch(`ajax_get_template.php?st_id=${templateId}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
// 기존 질문들 초기화
|
||||
document.getElementById('questionsList').innerHTML = '';
|
||||
questionCount = 0;
|
||||
|
||||
// 템플릿 질문들 추가
|
||||
data.questions.forEach((question, index) => {
|
||||
addTemplateQuestion(question, index + 1);
|
||||
});
|
||||
|
||||
// 기본 정보도 템플릿에서 가져오기
|
||||
if (data.template.st_name) {
|
||||
document.querySelector('input[name="sv_title"]').value = data.template.st_name;
|
||||
}
|
||||
if (data.template.st_description) {
|
||||
document.querySelector('textarea[name="sv_description"]').value = data.template.st_description;
|
||||
}
|
||||
|
||||
alert('템플릿이 적용되었습니다. 질문 설정 탭에서 확인해보세요.');
|
||||
} else {
|
||||
alert('템플릿 로드 실패: ' + data.message);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
alert('템플릿 로드 중 오류가 발생했습니다.');
|
||||
});
|
||||
}
|
||||
|
||||
// 템플릿 질문 추가
|
||||
function addTemplateQuestion(questionData, questionNumber) {
|
||||
questionCount = questionNumber;
|
||||
|
||||
const optionsHtml = ['radio', 'checkbox', 'select'].includes(questionData.stq_type) && questionData.stq_options.length > 0
|
||||
? `<div class="options-container" style="display: block;">
|
||||
<label class="form-label">선택지</label>
|
||||
<div class="options-list">
|
||||
${questionData.stq_options.map((option, index) => `
|
||||
<div class="option-item">
|
||||
<input type="text" name="questions[${questionNumber}][options][]" class="option-input" value="${option}" placeholder="선택지 ${index + 1}">
|
||||
<button type="button" class="btn-sm btn-danger" onclick="removeOption(this)">삭제</button>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
<button type="button" class="add-option-btn" onclick="addOption(this)">선택지 추가</button>
|
||||
</div>`
|
||||
: `<div class="options-container" style="display: none;">
|
||||
<label class="form-label">선택지</label>
|
||||
<div class="options-list">
|
||||
<div class="option-item">
|
||||
<input type="text" name="questions[${questionNumber}][options][]" class="option-input" placeholder="선택지 1">
|
||||
<button type="button" class="btn-sm btn-danger" onclick="removeOption(this)">삭제</button>
|
||||
</div>
|
||||
<div class="option-item">
|
||||
<input type="text" name="questions[${questionNumber}][options][]" class="option-input" placeholder="선택지 2">
|
||||
<button type="button" class="btn-sm btn-danger" onclick="removeOption(this)">삭제</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="add-option-btn" onclick="addOption(this)">선택지 추가</button>
|
||||
</div>`;
|
||||
|
||||
const questionHtml = `
|
||||
<div class="question-item" data-question-index="${questionNumber}">
|
||||
<div class="question-header">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<div class="question-number">${questionNumber}</div>
|
||||
<div style="flex: 1;">
|
||||
<select name="questions[${questionNumber}][type]" class="question-type-select form-select" onchange="updateQuestionType(this)">
|
||||
<option value="text" ${questionData.stq_type === 'text' ? 'selected' : ''}>단답형</option>
|
||||
<option value="textarea" ${questionData.stq_type === 'textarea' ? 'selected' : ''}>장문형</option>
|
||||
<option value="radio" ${questionData.stq_type === 'radio' ? 'selected' : ''}>단일선택</option>
|
||||
<option value="checkbox" ${questionData.stq_type === 'checkbox' ? 'selected' : ''}>다중선택</option>
|
||||
<option value="select" ${questionData.stq_type === 'select' ? 'selected' : ''}>드롭다운</option>
|
||||
<option value="rating" ${questionData.stq_type === 'rating' ? 'selected' : ''}>평점</option>
|
||||
<option value="date" ${questionData.stq_type === 'date' ? 'selected' : ''}>날짜</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="question-controls">
|
||||
<button type="button" class="btn-sm btn-secondary" onclick="moveQuestion(this, 'up')">↑</button>
|
||||
<button type="button" class="btn-sm btn-secondary" onclick="moveQuestion(this, 'down')">↓</button>
|
||||
<button type="button" class="btn-sm btn-danger" onclick="removeQuestion(this)">삭제</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">질문 제목</label>
|
||||
<input type="text" name="questions[${questionNumber}][title]" class="form-input"
|
||||
value="${questionData.stq_title}" placeholder="질문을 입력하세요" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">질문 설명 (선택사항)</label>
|
||||
<textarea name="questions[${questionNumber}][description]" class="form-textarea"
|
||||
placeholder="질문에 대한 추가 설명">${questionData.stq_description || ''}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="checkbox-item">
|
||||
<input type="checkbox" name="questions[${questionNumber}][required]" value="1" ${questionData.stq_required ? 'checked' : ''}>
|
||||
<label>필수 질문</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${optionsHtml}
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.getElementById('questionsList').insertAdjacentHTML('beforeend', questionHtml);
|
||||
}
|
||||
|
||||
// 질문 추가
|
||||
function addQuestion() {
|
||||
questionCount++;
|
||||
const questionHtml = `
|
||||
<div class="question-item" data-question-index="${questionCount}">
|
||||
<div class="question-header">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<div class="question-number">${questionCount}</div>
|
||||
<div style="flex: 1;">
|
||||
<select name="questions[${questionCount}][type]" class="question-type-select form-select" onchange="updateQuestionType(this)">
|
||||
<option value="text">단답형</option>
|
||||
<option value="textarea">장문형</option>
|
||||
<option value="radio">단일선택</option>
|
||||
<option value="checkbox">다중선택</option>
|
||||
<option value="select">드롭다운</option>
|
||||
<option value="rating">평점</option>
|
||||
<option value="date">날짜</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="question-controls">
|
||||
<button type="button" class="btn-sm btn-secondary" onclick="moveQuestion(this, 'up')">↑</button>
|
||||
<button type="button" class="btn-sm btn-secondary" onclick="moveQuestion(this, 'down')">↓</button>
|
||||
<button type="button" class="btn-sm btn-danger" onclick="removeQuestion(this)">삭제</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">질문 제목</label>
|
||||
<input type="text" name="questions[${questionCount}][title]" class="form-input" placeholder="질문을 입력하세요" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">질문 설명 (선택사항)</label>
|
||||
<textarea name="questions[${questionCount}][description]" class="form-textarea" placeholder="질문에 대한 추가 설명"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="checkbox-item">
|
||||
<input type="checkbox" name="questions[${questionCount}][required]" value="1">
|
||||
<label>필수 질문</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="options-container" style="display: none;">
|
||||
<label class="form-label">선택지</label>
|
||||
<div class="options-list">
|
||||
<div class="option-item">
|
||||
<input type="text" name="questions[${questionCount}][options][]" class="option-input" placeholder="선택지 1">
|
||||
<button type="button" class="btn-sm btn-danger" onclick="removeOption(this)">삭제</button>
|
||||
</div>
|
||||
<div class="option-item">
|
||||
<input type="text" name="questions[${questionCount}][options][]" class="option-input" placeholder="선택지 2">
|
||||
<button type="button" class="btn-sm btn-danger" onclick="removeOption(this)">삭제</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="add-option-btn" onclick="addOption(this)">선택지 추가</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.getElementById('questionsList').insertAdjacentHTML('beforeend', questionHtml);
|
||||
updateQuestionNumbers();
|
||||
}
|
||||
|
||||
// 질문 타입 변경
|
||||
function updateQuestionType(select) {
|
||||
const questionItem = select.closest('.question-item');
|
||||
const optionsContainer = questionItem.querySelector('.options-container');
|
||||
const questionType = select.value;
|
||||
|
||||
if (['radio', 'checkbox', 'select'].includes(questionType)) {
|
||||
optionsContainer.style.display = 'block';
|
||||
} else {
|
||||
optionsContainer.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// 질문 삭제
|
||||
function removeQuestion(button) {
|
||||
if (confirm('이 질문을 삭제하시겠습니까?')) {
|
||||
button.closest('.question-item').remove();
|
||||
updateQuestionNumbers();
|
||||
}
|
||||
}
|
||||
|
||||
// 질문 순서 변경
|
||||
function moveQuestion(button, direction) {
|
||||
const questionItem = button.closest('.question-item');
|
||||
const sibling = direction === 'up' ? questionItem.previousElementSibling : questionItem.nextElementSibling;
|
||||
|
||||
if (sibling) {
|
||||
if (direction === 'up') {
|
||||
questionItem.parentNode.insertBefore(questionItem, sibling);
|
||||
} else {
|
||||
questionItem.parentNode.insertBefore(sibling, questionItem);
|
||||
}
|
||||
updateQuestionNumbers();
|
||||
}
|
||||
}
|
||||
|
||||
// 선택지 추가
|
||||
function addOption(button) {
|
||||
const optionsList = button.previousElementSibling;
|
||||
const questionIndex = button.closest('.question-item').dataset.questionIndex;
|
||||
const optionCount = optionsList.children.length + 1;
|
||||
|
||||
const optionHtml = `
|
||||
<div class="option-item">
|
||||
<input type="text" name="questions[${questionIndex}][options][]" class="option-input" placeholder="선택지 ${optionCount}">
|
||||
<button type="button" class="btn-sm btn-danger" onclick="removeOption(this)">삭제</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
optionsList.insertAdjacentHTML('beforeend', optionHtml);
|
||||
}
|
||||
|
||||
// 선택지 삭제
|
||||
function removeOption(button) {
|
||||
const optionsList = button.closest('.options-list');
|
||||
if (optionsList.children.length > 2) {
|
||||
button.closest('.option-item').remove();
|
||||
} else {
|
||||
alert('최소 2개의 선택지가 필요합니다.');
|
||||
}
|
||||
}
|
||||
|
||||
// 질문 번호 업데이트
|
||||
function updateQuestionNumbers() {
|
||||
const questions = document.querySelectorAll('.question-item');
|
||||
questions.forEach((question, index) => {
|
||||
const number = index + 1;
|
||||
question.querySelector('.question-number').textContent = number;
|
||||
question.dataset.questionIndex = number;
|
||||
|
||||
// input name 속성 업데이트
|
||||
const inputs = question.querySelectorAll('input, textarea, select');
|
||||
inputs.forEach(input => {
|
||||
if (input.name && input.name.includes('questions[')) {
|
||||
input.name = input.name.replace(/questions\[\d+\]/, `questions[${number}]`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
questionCount = questions.length;
|
||||
}
|
||||
|
||||
// 폼 검증
|
||||
function initFormValidation() {
|
||||
document.getElementById('surveyForm').addEventListener('submit', function(e) {
|
||||
const title = document.querySelector('input[name="sv_title"]').value.trim();
|
||||
const startDate = new Date(document.querySelector('input[name="sv_start_date"]').value);
|
||||
const endDate = new Date(document.querySelector('input[name="sv_end_date"]').value);
|
||||
|
||||
if (!title) {
|
||||
alert('설문 제목을 입력해주세요.');
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if (startDate >= endDate) {
|
||||
alert('종료일시는 시작일시보다 늦어야 합니다.');
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
const questions = document.querySelectorAll('.question-item');
|
||||
if (questions.length === 0) {
|
||||
alert('최소 1개의 질문을 추가해주세요.');
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
// 질문 제목 검증
|
||||
let hasEmptyQuestion = false;
|
||||
questions.forEach(question => {
|
||||
const titleInput = question.querySelector('input[name*="[title]"]');
|
||||
if (!titleInput.value.trim()) {
|
||||
hasEmptyQuestion = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (hasEmptyQuestion) {
|
||||
alert('모든 질문의 제목을 입력해주세요.');
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user