- 설문 테이블 스키마 추가 (survey_templates, survey_progress, survey_responses) - 설문 템플릿 데이터 추가 스크립트 - 모바일 문진표 HTML 템플릿 ※ 아직 개발 중인 기능으로 추가 구현 필요
882 lines
28 KiB
HTML
882 lines
28 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="ko">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
|
<title>건강 상담 사전 문진표</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css">
|
|
<style>
|
|
:root {
|
|
--primary-color: #4CAF50;
|
|
--secondary-color: #2196F3;
|
|
--accent-color: #FF9800;
|
|
--text-dark: #333;
|
|
--bg-light: #f5f5f5;
|
|
}
|
|
|
|
* {
|
|
-webkit-tap-highlight-color: transparent;
|
|
}
|
|
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
min-height: 100vh;
|
|
padding: 0;
|
|
margin: 0;
|
|
}
|
|
|
|
.survey-container {
|
|
max-width: 600px;
|
|
margin: 0 auto;
|
|
padding: 0;
|
|
min-height: 100vh;
|
|
background: white;
|
|
box-shadow: 0 0 30px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
.survey-header {
|
|
background: linear-gradient(135deg, var(--primary-color) 0%, #45a049 100%);
|
|
color: white;
|
|
padding: 20px;
|
|
text-align: center;
|
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 100;
|
|
}
|
|
|
|
.survey-header h1 {
|
|
font-size: 1.5rem;
|
|
margin: 0;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.survey-header p {
|
|
margin: 5px 0 0 0;
|
|
font-size: 0.9rem;
|
|
opacity: 0.95;
|
|
}
|
|
|
|
.progress-bar-container {
|
|
background: white;
|
|
padding: 15px 20px;
|
|
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
|
|
position: sticky;
|
|
top: 90px;
|
|
z-index: 99;
|
|
}
|
|
|
|
.progress {
|
|
height: 8px;
|
|
border-radius: 10px;
|
|
background: #e0e0e0;
|
|
}
|
|
|
|
.progress-bar {
|
|
background: linear-gradient(90deg, var(--primary-color), var(--secondary-color));
|
|
border-radius: 10px;
|
|
transition: width 0.3s ease;
|
|
}
|
|
|
|
.progress-text {
|
|
font-size: 0.85rem;
|
|
color: #666;
|
|
margin-top: 5px;
|
|
text-align: center;
|
|
}
|
|
|
|
.category-section {
|
|
padding: 20px;
|
|
}
|
|
|
|
.category-header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
margin-bottom: 20px;
|
|
padding-bottom: 10px;
|
|
border-bottom: 2px solid var(--primary-color);
|
|
}
|
|
|
|
.category-icon {
|
|
width: 40px;
|
|
height: 40px;
|
|
background: linear-gradient(135deg, var(--primary-color), #45a049);
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: white;
|
|
font-size: 1.2rem;
|
|
}
|
|
|
|
.category-title {
|
|
flex: 1;
|
|
font-size: 1.3rem;
|
|
font-weight: 600;
|
|
color: var(--text-dark);
|
|
}
|
|
|
|
.question-card {
|
|
background: white;
|
|
border: 1px solid #e0e0e0;
|
|
border-radius: 12px;
|
|
padding: 20px;
|
|
margin-bottom: 20px;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.question-card:hover {
|
|
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.question-text {
|
|
font-size: 1.05rem;
|
|
font-weight: 500;
|
|
color: var(--text-dark);
|
|
margin-bottom: 5px;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.question-required {
|
|
color: #f44336;
|
|
margin-left: 4px;
|
|
}
|
|
|
|
.question-subtext {
|
|
font-size: 0.85rem;
|
|
color: #757575;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
/* Radio/Checkbox 스타일 */
|
|
.option-group {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
}
|
|
|
|
.option-label {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 12px 15px;
|
|
background: #f9f9f9;
|
|
border: 2px solid #e0e0e0;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
transition: all 0.2s ease;
|
|
user-select: none;
|
|
}
|
|
|
|
.option-label:active {
|
|
transform: scale(0.98);
|
|
}
|
|
|
|
.option-label input {
|
|
margin-right: 12px;
|
|
width: 20px;
|
|
height: 20px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.option-label:hover {
|
|
background: #f0f7ff;
|
|
border-color: var(--secondary-color);
|
|
}
|
|
|
|
.option-label input:checked + span {
|
|
font-weight: 600;
|
|
color: var(--primary-color);
|
|
}
|
|
|
|
.option-label:has(input:checked) {
|
|
background: #e8f5e9;
|
|
border-color: var(--primary-color);
|
|
}
|
|
|
|
/* Text/Number 입력 */
|
|
.form-control {
|
|
border: 2px solid #e0e0e0;
|
|
border-radius: 8px;
|
|
padding: 12px 15px;
|
|
font-size: 1rem;
|
|
transition: border-color 0.3s ease;
|
|
}
|
|
|
|
.form-control:focus {
|
|
border-color: var(--primary-color);
|
|
box-shadow: 0 0 0 3px rgba(76, 175, 80, 0.1);
|
|
}
|
|
|
|
/* 버튼 */
|
|
.btn-group-sticky {
|
|
position: sticky;
|
|
bottom: 0;
|
|
background: white;
|
|
padding: 15px 20px;
|
|
box-shadow: 0 -2px 10px rgba(0,0,0,0.1);
|
|
display: flex;
|
|
gap: 10px;
|
|
z-index: 98;
|
|
}
|
|
|
|
.btn {
|
|
flex: 1;
|
|
padding: 14px;
|
|
border-radius: 8px;
|
|
font-size: 1rem;
|
|
font-weight: 600;
|
|
border: none;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.btn-primary {
|
|
background: linear-gradient(135deg, var(--primary-color), #45a049);
|
|
color: white;
|
|
}
|
|
|
|
.btn-primary:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 12px rgba(76, 175, 80, 0.3);
|
|
}
|
|
|
|
.btn-secondary {
|
|
background: #f5f5f5;
|
|
color: #666;
|
|
}
|
|
|
|
.btn-save {
|
|
background: linear-gradient(135deg, var(--accent-color), #f57c00);
|
|
color: white;
|
|
}
|
|
|
|
.btn-complete {
|
|
background: linear-gradient(135deg, var(--secondary-color), #1976d2);
|
|
color: white;
|
|
}
|
|
|
|
/* 로딩 스피너 */
|
|
.loading {
|
|
text-align: center;
|
|
padding: 40px;
|
|
}
|
|
|
|
.spinner-border {
|
|
width: 3rem;
|
|
height: 3rem;
|
|
border-width: 0.3em;
|
|
}
|
|
|
|
/* 완료 화면 */
|
|
.completion-screen {
|
|
text-align: center;
|
|
padding: 60px 20px;
|
|
}
|
|
|
|
.completion-icon {
|
|
width: 100px;
|
|
height: 100px;
|
|
background: linear-gradient(135deg, var(--primary-color), #45a049);
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin: 0 auto 20px;
|
|
animation: scaleIn 0.5s ease;
|
|
}
|
|
|
|
.completion-icon i {
|
|
font-size: 3rem;
|
|
color: white;
|
|
}
|
|
|
|
@keyframes scaleIn {
|
|
from {
|
|
transform: scale(0);
|
|
}
|
|
to {
|
|
transform: scale(1);
|
|
}
|
|
}
|
|
|
|
.completion-title {
|
|
font-size: 1.5rem;
|
|
font-weight: 600;
|
|
color: var(--text-dark);
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.completion-message {
|
|
font-size: 1rem;
|
|
color: #757575;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
/* 카테고리 네비게이션 */
|
|
.category-nav {
|
|
display: flex;
|
|
overflow-x: auto;
|
|
gap: 10px;
|
|
padding: 15px 20px;
|
|
background: white;
|
|
border-bottom: 1px solid #e0e0e0;
|
|
position: sticky;
|
|
top: 90px;
|
|
z-index: 97;
|
|
-webkit-overflow-scrolling: touch;
|
|
}
|
|
|
|
.category-nav::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
|
|
.category-nav-item {
|
|
flex-shrink: 0;
|
|
padding: 8px 16px;
|
|
border-radius: 20px;
|
|
background: #f5f5f5;
|
|
color: #666;
|
|
font-size: 0.9rem;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.category-nav-item.active {
|
|
background: var(--primary-color);
|
|
color: white;
|
|
}
|
|
|
|
.category-nav-item.completed {
|
|
background: #e8f5e9;
|
|
color: var(--primary-color);
|
|
}
|
|
|
|
/* 반응형 */
|
|
@media (max-width: 576px) {
|
|
.survey-header h1 {
|
|
font-size: 1.3rem;
|
|
}
|
|
|
|
.question-card {
|
|
padding: 15px;
|
|
}
|
|
|
|
.option-label {
|
|
padding: 10px 12px;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="survey-container">
|
|
<!-- 헤더 -->
|
|
<div class="survey-header">
|
|
<h1><i class="bi bi-clipboard2-pulse"></i> 건강 상담 사전 문진표</h1>
|
|
<p id="patientInfo">환자명: <span id="patientName">-</span></p>
|
|
</div>
|
|
|
|
<!-- 진행률 -->
|
|
<div class="progress-bar-container">
|
|
<div class="progress">
|
|
<div class="progress-bar" role="progressbar" style="width: 0%" id="progressBar"></div>
|
|
</div>
|
|
<div class="progress-text">
|
|
<span id="progressText">0 / 0</span> 완료
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 카테고리 네비게이션 -->
|
|
<div class="category-nav" id="categoryNav" style="display: none;">
|
|
<!-- 동적 생성 -->
|
|
</div>
|
|
|
|
<!-- 메인 컨텐츠 -->
|
|
<div id="surveyContent">
|
|
<!-- 로딩 -->
|
|
<div class="loading" id="loadingScreen">
|
|
<div class="spinner-border text-primary" role="status">
|
|
<span class="visually-hidden">Loading...</span>
|
|
</div>
|
|
<p class="mt-3">문진표를 불러오는 중...</p>
|
|
</div>
|
|
|
|
<!-- 문진 폼 -->
|
|
<div id="surveyForm" style="display: none;">
|
|
<!-- 동적으로 생성됨 -->
|
|
</div>
|
|
|
|
<!-- 완료 화면 -->
|
|
<div id="completionScreen" style="display: none;">
|
|
<div class="completion-screen">
|
|
<div class="completion-icon">
|
|
<i class="bi bi-check-circle-fill"></i>
|
|
</div>
|
|
<h2 class="completion-title">제출 완료!</h2>
|
|
<p class="completion-message">
|
|
문진표가 성공적으로 제출되었습니다.<br>
|
|
담당 한의사가 확인 후 연락드리겠습니다.<br>
|
|
감사합니다.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 하단 버튼 -->
|
|
<div class="btn-group-sticky" id="buttonGroup" style="display: none;">
|
|
<button class="btn btn-save" id="saveBtn">
|
|
<i class="bi bi-bookmark"></i> 임시저장
|
|
</button>
|
|
<button class="btn btn-complete" id="completeBtn">
|
|
<i class="bi bi-check-circle"></i> 제출하기
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script>
|
|
const surveyToken = window.location.pathname.split('/').pop();
|
|
let surveyData = null;
|
|
let categories = [];
|
|
let templates = [];
|
|
let responses = {};
|
|
let currentCategoryIndex = 0;
|
|
|
|
$(document).ready(function() {
|
|
loadSurvey();
|
|
});
|
|
|
|
async function loadSurvey() {
|
|
try {
|
|
// 문진표 정보 로드
|
|
const surveyResponse = await $.get(`/api/surveys/${surveyToken}`);
|
|
surveyData = surveyResponse.data;
|
|
|
|
// 환자 정보 표시
|
|
if (surveyData.patient_name) {
|
|
$('#patientName').text(surveyData.patient_name);
|
|
}
|
|
|
|
// 카테고리 목록 로드
|
|
const categoriesResponse = await $.get('/api/surveys/categories');
|
|
categories = categoriesResponse.data;
|
|
|
|
// 템플릿 로드
|
|
const templatesResponse = await $.get('/api/surveys/templates');
|
|
templates = templatesResponse.data;
|
|
|
|
// 기존 응답 로드
|
|
if (surveyData.responses && surveyData.responses.length > 0) {
|
|
surveyData.responses.forEach(resp => {
|
|
try {
|
|
responses[resp.question_code] = JSON.parse(resp.answer_value);
|
|
} catch (e) {
|
|
responses[resp.question_code] = resp.answer_value;
|
|
}
|
|
});
|
|
}
|
|
|
|
// UI 렌더링
|
|
renderCategoryNav();
|
|
renderSurveyForm();
|
|
updateProgress();
|
|
|
|
$('#loadingScreen').hide();
|
|
$('#surveyForm').show();
|
|
$('#buttonGroup').show();
|
|
$('#categoryNav').show();
|
|
|
|
} catch (error) {
|
|
console.error('Error loading survey:', error);
|
|
alert('문진표를 불러오는데 실패했습니다.');
|
|
}
|
|
}
|
|
|
|
function renderCategoryNav() {
|
|
const nav = $('#categoryNav');
|
|
nav.empty();
|
|
|
|
categories.forEach((cat, index) => {
|
|
const navItem = $(`
|
|
<div class="category-nav-item" data-index="${index}">
|
|
${cat.category_name}
|
|
</div>
|
|
`);
|
|
|
|
navItem.on('click', function() {
|
|
scrollToCategory(index);
|
|
});
|
|
|
|
nav.append(navItem);
|
|
});
|
|
}
|
|
|
|
function renderSurveyForm() {
|
|
const form = $('#surveyForm');
|
|
form.empty();
|
|
|
|
categories.forEach((cat, catIndex) => {
|
|
const categoryQuestions = templates.filter(t => t.category === cat.category);
|
|
|
|
const section = $(`
|
|
<div class="category-section" id="category-${catIndex}">
|
|
<div class="category-header">
|
|
<div class="category-icon">
|
|
<i class="bi bi-${getCategoryIcon(cat.category)}"></i>
|
|
</div>
|
|
<div class="category-title">${cat.category_name}</div>
|
|
</div>
|
|
</div>
|
|
`);
|
|
|
|
categoryQuestions.forEach(question => {
|
|
const questionCard = renderQuestion(question);
|
|
section.append(questionCard);
|
|
});
|
|
|
|
form.append(section);
|
|
});
|
|
}
|
|
|
|
function renderQuestion(question) {
|
|
const card = $(`
|
|
<div class="question-card" data-code="${question.question_code}">
|
|
<div class="question-text">
|
|
${question.question_text}
|
|
${question.is_required ? '<span class="question-required">*</span>' : ''}
|
|
</div>
|
|
${question.question_subtext ? `<div class="question-subtext">${question.question_subtext}</div>` : ''}
|
|
<div class="answer-area"></div>
|
|
</div>
|
|
`);
|
|
|
|
const answerArea = card.find('.answer-area');
|
|
const savedValue = responses[question.question_code];
|
|
|
|
switch (question.input_type) {
|
|
case 'RADIO':
|
|
answerArea.append(renderRadio(question, savedValue));
|
|
break;
|
|
case 'CHECKBOX':
|
|
answerArea.append(renderCheckbox(question, savedValue));
|
|
break;
|
|
case 'TEXT':
|
|
case 'TEXTAREA':
|
|
answerArea.append(renderText(question, savedValue));
|
|
break;
|
|
case 'NUMBER':
|
|
answerArea.append(renderNumber(question, savedValue));
|
|
break;
|
|
}
|
|
|
|
return card;
|
|
}
|
|
|
|
function renderRadio(question, savedValue) {
|
|
const group = $('<div class="option-group"></div>');
|
|
|
|
question.options.forEach((option, index) => {
|
|
const isChecked = savedValue === option ? 'checked' : '';
|
|
const label = $(`
|
|
<label class="option-label">
|
|
<input type="radio"
|
|
name="${question.question_code}"
|
|
value="${option}"
|
|
${isChecked}
|
|
data-code="${question.question_code}"
|
|
data-category="${question.category}">
|
|
<span>${option}</span>
|
|
</label>
|
|
`);
|
|
|
|
label.find('input').on('change', function() {
|
|
saveResponse(question.question_code, question.category, this.value, 'SINGLE');
|
|
updateProgress();
|
|
});
|
|
|
|
group.append(label);
|
|
});
|
|
|
|
return group;
|
|
}
|
|
|
|
function renderCheckbox(question, savedValue) {
|
|
const group = $('<div class="option-group"></div>');
|
|
const savedArray = Array.isArray(savedValue) ? savedValue : [];
|
|
|
|
question.options.forEach((option, index) => {
|
|
const isChecked = savedArray.includes(option) ? 'checked' : '';
|
|
const label = $(`
|
|
<label class="option-label">
|
|
<input type="checkbox"
|
|
name="${question.question_code}[]"
|
|
value="${option}"
|
|
${isChecked}
|
|
data-code="${question.question_code}"
|
|
data-category="${question.category}">
|
|
<span>${option}</span>
|
|
</label>
|
|
`);
|
|
|
|
label.find('input').on('change', function() {
|
|
const checked = $(`input[name="${question.question_code}[]"]:checked`).map(function() {
|
|
return $(this).val();
|
|
}).get();
|
|
saveResponse(question.question_code, question.category, checked, 'MULTIPLE');
|
|
updateProgress();
|
|
});
|
|
|
|
group.append(label);
|
|
});
|
|
|
|
return group;
|
|
}
|
|
|
|
function renderText(question, savedValue) {
|
|
const isTextarea = question.input_type === 'TEXTAREA';
|
|
const input = $(`
|
|
<${isTextarea ? 'textarea' : 'input'}
|
|
type="text"
|
|
class="form-control"
|
|
placeholder="답변을 입력하세요"
|
|
data-code="${question.question_code}"
|
|
data-category="${question.category}"
|
|
${isTextarea ? 'rows="3"' : ''}
|
|
>${savedValue || ''}</${isTextarea ? 'textarea' : 'input'}>
|
|
`);
|
|
|
|
input.on('blur', function() {
|
|
const value = $(this).val().trim();
|
|
if (value) {
|
|
saveResponse(question.question_code, question.category, value, 'TEXT');
|
|
updateProgress();
|
|
}
|
|
});
|
|
|
|
return input;
|
|
}
|
|
|
|
function renderNumber(question, savedValue) {
|
|
const input = $(`
|
|
<input type="number"
|
|
class="form-control"
|
|
placeholder="숫자를 입력하세요"
|
|
value="${savedValue || ''}"
|
|
data-code="${question.question_code}"
|
|
data-category="${question.category}">
|
|
`);
|
|
|
|
input.on('blur', function() {
|
|
const value = $(this).val();
|
|
if (value) {
|
|
saveResponse(question.question_code, question.category, value, 'NUMBER');
|
|
updateProgress();
|
|
}
|
|
});
|
|
|
|
return input;
|
|
}
|
|
|
|
function saveResponse(questionCode, category, value, answerType) {
|
|
responses[questionCode] = value;
|
|
|
|
// 로컬 스토리지에도 저장 (임시저장용)
|
|
localStorage.setItem(`survey_${surveyToken}`, JSON.stringify(responses));
|
|
}
|
|
|
|
async function saveToServer() {
|
|
try {
|
|
const responseArray = [];
|
|
|
|
Object.keys(responses).forEach(code => {
|
|
const template = templates.find(t => t.question_code === code);
|
|
if (template) {
|
|
responseArray.push({
|
|
category: template.category,
|
|
question_code: code,
|
|
question_text: template.question_text,
|
|
answer_value: responses[code],
|
|
answer_type: Array.isArray(responses[code]) ? 'MULTIPLE' : 'SINGLE'
|
|
});
|
|
}
|
|
});
|
|
|
|
await $.ajax({
|
|
url: `/api/surveys/${surveyToken}/responses`,
|
|
method: 'POST',
|
|
contentType: 'application/json',
|
|
data: JSON.stringify({ responses: responseArray })
|
|
});
|
|
|
|
return true;
|
|
} catch (error) {
|
|
console.error('Save error:', error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function updateProgress() {
|
|
const totalQuestions = templates.filter(t => t.is_required).length;
|
|
const answeredQuestions = templates.filter(t =>
|
|
t.is_required && responses[t.question_code] !== undefined && responses[t.question_code] !== ''
|
|
).length;
|
|
|
|
const percentage = totalQuestions > 0 ? (answeredQuestions / totalQuestions * 100) : 0;
|
|
|
|
$('#progressBar').css('width', percentage + '%');
|
|
$('#progressText').text(`${answeredQuestions} / ${totalQuestions}`);
|
|
|
|
// 카테고리별 완료 상태 업데이트
|
|
updateCategoryStatus();
|
|
}
|
|
|
|
function updateCategoryStatus() {
|
|
categories.forEach((cat, index) => {
|
|
const catQuestions = templates.filter(t => t.category === cat.category && t.is_required);
|
|
const catAnswered = catQuestions.filter(q => responses[q.question_code] !== undefined).length;
|
|
const isCompleted = catAnswered === catQuestions.length && catQuestions.length > 0;
|
|
|
|
const navItem = $(`.category-nav-item[data-index="${index}"]`);
|
|
if (isCompleted) {
|
|
navItem.addClass('completed');
|
|
}
|
|
});
|
|
}
|
|
|
|
function scrollToCategory(index) {
|
|
const target = $(`#category-${index}`);
|
|
if (target.length) {
|
|
$('html, body').animate({
|
|
scrollTop: target.offset().top - 160
|
|
}, 300);
|
|
|
|
$('.category-nav-item').removeClass('active');
|
|
$(`.category-nav-item[data-index="${index}"]`).addClass('active');
|
|
}
|
|
}
|
|
|
|
function getCategoryIcon(category) {
|
|
const icons = {
|
|
'TEMPERATURE': 'thermometer-half',
|
|
'BODY_TEMP': 'heart-pulse',
|
|
'DIGESTION': 'cup-straw',
|
|
'EXCRETION': 'water',
|
|
'SLEEP': 'moon-stars',
|
|
'CIRCULATION': 'activity',
|
|
'WOMEN_HEALTH': 'gender-female',
|
|
'SKIN': 'palette',
|
|
'PERSONALITY': 'person-circle'
|
|
};
|
|
return icons[category] || 'question-circle';
|
|
}
|
|
|
|
// 임시저장 버튼
|
|
$('#saveBtn').on('click', async function() {
|
|
const btn = $(this);
|
|
btn.prop('disabled', true).html('<i class="bi bi-hourglass-split"></i> 저장중...');
|
|
|
|
const success = await saveToServer();
|
|
|
|
if (success) {
|
|
btn.html('<i class="bi bi-check"></i> 저장됨');
|
|
setTimeout(() => {
|
|
btn.prop('disabled', false).html('<i class="bi bi-bookmark"></i> 임시저장');
|
|
}, 2000);
|
|
} else {
|
|
alert('저장에 실패했습니다.');
|
|
btn.prop('disabled', false).html('<i class="bi bi-bookmark"></i> 임시저장');
|
|
}
|
|
});
|
|
|
|
// 제출 버튼
|
|
$('#completeBtn').on('click', async function() {
|
|
// 필수 항목 체크
|
|
const requiredQuestions = templates.filter(t => t.is_required);
|
|
const unanswered = requiredQuestions.filter(q =>
|
|
responses[q.question_code] === undefined || responses[q.question_code] === ''
|
|
);
|
|
|
|
if (unanswered.length > 0) {
|
|
alert(`필수 항목 ${unanswered.length}개가 미응답 상태입니다.\n모든 필수 항목을 작성해주세요.`);
|
|
return;
|
|
}
|
|
|
|
if (!confirm('문진표를 제출하시겠습니까?')) {
|
|
return;
|
|
}
|
|
|
|
const btn = $(this);
|
|
btn.prop('disabled', true).html('<i class="bi bi-hourglass-split"></i> 제출중...');
|
|
|
|
// 서버에 저장
|
|
const saveSuccess = await saveToServer();
|
|
if (!saveSuccess) {
|
|
alert('저장에 실패했습니다.');
|
|
btn.prop('disabled', false).html('<i class="bi bi-check-circle"></i> 제출하기');
|
|
return;
|
|
}
|
|
|
|
// 완료 처리
|
|
try {
|
|
await $.ajax({
|
|
url: `/api/surveys/${surveyToken}/complete`,
|
|
method: 'POST'
|
|
});
|
|
|
|
// 완료 화면 표시
|
|
$('#surveyForm').hide();
|
|
$('#buttonGroup').hide();
|
|
$('#categoryNav').hide();
|
|
$('#completionScreen').show();
|
|
|
|
// 로컬 스토리지 삭제
|
|
localStorage.removeItem(`survey_${surveyToken}`);
|
|
|
|
} catch (error) {
|
|
alert('제출에 실패했습니다.');
|
|
btn.prop('disabled', false).html('<i class="bi bi-check-circle"></i> 제출하기');
|
|
}
|
|
});
|
|
|
|
// 스크롤 이벤트로 현재 카테고리 하이라이트
|
|
$(window).on('scroll', function() {
|
|
const scrollPos = $(window).scrollTop() + 200;
|
|
|
|
$('.category-section').each(function(index) {
|
|
const top = $(this).offset().top;
|
|
const bottom = top + $(this).outerHeight();
|
|
|
|
if (scrollPos >= top && scrollPos < bottom) {
|
|
$('.category-nav-item').removeClass('active');
|
|
$(`.category-nav-item[data-index="${index}"]`).addClass('active');
|
|
}
|
|
});
|
|
});
|
|
|
|
// 로컬 스토리지에서 임시 저장된 데이터 복원
|
|
$(document).ready(function() {
|
|
const saved = localStorage.getItem(`survey_${surveyToken}`);
|
|
if (saved) {
|
|
try {
|
|
const savedResponses = JSON.parse(saved);
|
|
if (Object.keys(savedResponses).length > Object.keys(responses).length) {
|
|
if (confirm('이전에 작성 중이던 내용이 있습니다. 불러오시겠습니까?')) {
|
|
responses = savedResponses;
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.error('Failed to restore saved data:', e);
|
|
}
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|