first commit 2
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
if (!defined('_GNUBOARD_')) exit;
|
||||
|
||||
// 디버깅: config.php 로드됨
|
||||
error_log('survey_form config.php loaded');
|
||||
|
||||
// 설문 목록 가져오기
|
||||
$survey_options = array();
|
||||
$survey_sql = "SELECT sv_id, sv_title, sv_status FROM survey_master ORDER BY sv_created_at DESC";
|
||||
$survey_result = sql_query($survey_sql);
|
||||
while ($survey = sql_fetch_array($survey_result)) {
|
||||
$status_text = '';
|
||||
switch($survey['sv_status']) {
|
||||
case 'active': $status_text = ' (진행중)'; break;
|
||||
case 'draft': $status_text = ' (임시저장)'; break;
|
||||
case 'closed': $status_text = ' (종료)'; break;
|
||||
}
|
||||
$survey_options[$survey['sv_id']] = $survey['sv_title'] . $status_text;
|
||||
}
|
||||
|
||||
// 현재 설정값 가져오기
|
||||
$current_sv_id = '';
|
||||
if (isset($row_mod['md_custom_survey_key']) && $row_mod['md_custom_survey_key']) {
|
||||
$custom_options = json_decode($row_mod['md_custom_survey_key'], true);
|
||||
if (isset($custom_options['sv_id'])) {
|
||||
$current_sv_id = $custom_options['sv_id'];
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="module-config-section">
|
||||
<h4><i class="fa fa-clipboard"></i> 설문 폼 모듈 설정</h4>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="survey_select">표시할 설문 선택:</label>
|
||||
<select name="custom_options[sv_id]" id="survey_select" class="form-control">
|
||||
<option value="">-- 설문을 선택하세요 --</option>
|
||||
<?php foreach ($survey_options as $sv_id => $sv_title): ?>
|
||||
<option value="<?php echo $sv_id; ?>" <?php echo ($current_sv_id == $sv_id) ? 'selected' : ''; ?>>
|
||||
<?php echo htmlspecialchars($sv_title); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<small class="form-text text-muted">
|
||||
설문을 선택하지 않으면 사용자가 직접 설문을 선택할 수 있는 인터페이스가 표시됩니다.
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<?php if (empty($survey_options)): ?>
|
||||
<div class="alert alert-info">
|
||||
<i class="fa fa-info-circle"></i>
|
||||
등록된 설문이 없습니다.
|
||||
<a href="<?php echo G5_ADMIN_URL; ?>/survey_manage/survey_form.php" target="_blank">새 설문 만들기</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="form-group">
|
||||
<label>모듈 사용법:</label>
|
||||
<ul class="usage-list">
|
||||
<li>위에서 설문을 선택하면 해당 설문이 자동으로 표시됩니다.</li>
|
||||
<li>설문을 선택하지 않으면 사용자가 설문을 선택할 수 있는 목록이 표시됩니다.</li>
|
||||
<li>URL에 <code>?sv_id=설문번호</code>를 추가하면 특정 설문을 강제로 표시할 수 있습니다.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.module-config-section {
|
||||
padding: 20px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
background: #f9f9f9;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.module-config-section h4 {
|
||||
margin-top: 0;
|
||||
color: #333;
|
||||
border-bottom: 2px solid #AA20FF;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
font-weight: 600;
|
||||
margin-bottom: 5px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.form-text {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.alert {
|
||||
padding: 12px 15px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.alert-info {
|
||||
background-color: #d1ecf1;
|
||||
border-color: #bee5eb;
|
||||
color: #0c5460;
|
||||
}
|
||||
|
||||
.usage-list {
|
||||
margin: 0;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.usage-list li {
|
||||
margin-bottom: 5px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.usage-list code {
|
||||
background: #f1f1f1;
|
||||
padding: 2px 4px;
|
||||
border-radius: 3px;
|
||||
font-family: monospace;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const surveySelect = document.getElementById('survey_select');
|
||||
if (surveySelect) {
|
||||
surveySelect.addEventListener('change', function() {
|
||||
// 설문 선택 시 미리보기나 추가 정보를 표시할 수 있습니다.
|
||||
console.log('선택된 설문 ID:', this.value);
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,636 @@
|
||||
/* 설문 폼 모듈 스타일 */
|
||||
.survey-form-module {
|
||||
--survey-primary: #AA20FF;
|
||||
--survey-primary-light: rgba(170, 32, 255, 0.1);
|
||||
--survey-primary-dark: #8A1ACC;
|
||||
}
|
||||
|
||||
.survey-container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.survey-header {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
padding: 40px 20px;
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.survey-header::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 5px;
|
||||
background: linear-gradient(90deg, var(--survey-primary) 0%, var(--survey-primary-dark) 100%);
|
||||
}
|
||||
|
||||
.survey-title {
|
||||
font-size: 2.5em;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
margin-bottom: 15px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.survey-description {
|
||||
font-size: 1.1em;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.survey-info {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 30px;
|
||||
margin-top: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.survey-info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: #888;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.survey-info-item i {
|
||||
color: var(--survey-primary);
|
||||
}
|
||||
|
||||
.progress-container {
|
||||
position: sticky;
|
||||
top: 20px;
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
|
||||
margin-bottom: 30px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
background: #e0e0e0;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, var(--survey-primary) 0%, var(--survey-primary-dark) 100%);
|
||||
border-radius: 4px;
|
||||
transition: width 0.5s ease;
|
||||
width: 0%;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
font-size: 0.9em;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.survey-form {
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
padding: 40px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.question-container {
|
||||
margin-bottom: 40px;
|
||||
padding-bottom: 30px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
animation: fadeInUp 0.6s ease forwards;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.question-container:last-child {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.question-header {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.question-number {
|
||||
display: inline-block;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background: var(--survey-primary);
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
line-height: 30px;
|
||||
font-weight: bold;
|
||||
font-size: 0.9em;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.question-title {
|
||||
font-size: 1.3em;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
line-height: 1.4;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.question-required {
|
||||
color: #e74c3c;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.question-description {
|
||||
margin-top: 10px;
|
||||
color: #666;
|
||||
font-size: 0.95em;
|
||||
line-height: 1.5;
|
||||
margin-left: 45px;
|
||||
}
|
||||
|
||||
.question-input {
|
||||
margin-left: 45px;
|
||||
}
|
||||
|
||||
/* 텍스트 입력 */
|
||||
.form-input {
|
||||
width: 100%;
|
||||
padding: 15px 20px;
|
||||
border: 2px solid #e0e0e0;
|
||||
border-radius: 10px;
|
||||
font-size: 1em;
|
||||
transition: all 0.3s ease;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.form-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--survey-primary);
|
||||
background: white;
|
||||
box-shadow: 0 0 0 3px var(--survey-primary-light);
|
||||
}
|
||||
|
||||
.form-textarea {
|
||||
min-height: 120px;
|
||||
resize: vertical;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
/* 라디오 버튼 */
|
||||
.radio-group,
|
||||
.checkbox-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.radio-item,
|
||||
.checkbox-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 15px 20px;
|
||||
background: #fafafa;
|
||||
border: 2px solid #e0e0e0;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.radio-item:hover,
|
||||
.checkbox-item:hover {
|
||||
background: var(--survey-primary-light);
|
||||
border-color: var(--survey-primary);
|
||||
}
|
||||
|
||||
.radio-item.selected,
|
||||
.checkbox-item.selected {
|
||||
background: var(--survey-primary-light);
|
||||
border-color: var(--survey-primary);
|
||||
color: var(--survey-primary-dark);
|
||||
}
|
||||
|
||||
.radio-item input,
|
||||
.checkbox-item input {
|
||||
margin-right: 15px;
|
||||
transform: scale(1.2);
|
||||
accent-color: var(--survey-primary);
|
||||
}
|
||||
|
||||
.radio-item label,
|
||||
.checkbox-item label {
|
||||
flex: 1;
|
||||
cursor: pointer;
|
||||
font-size: 1em;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* 셀렉트 박스 */
|
||||
.form-select {
|
||||
width: 100%;
|
||||
padding: 15px 20px;
|
||||
border: 2px solid #e0e0e0;
|
||||
border-radius: 10px;
|
||||
font-size: 1em;
|
||||
background: #fafafa;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.form-select:focus {
|
||||
outline: none;
|
||||
border-color: var(--survey-primary);
|
||||
background: white;
|
||||
box-shadow: 0 0 0 3px var(--survey-primary-light);
|
||||
}
|
||||
|
||||
/* 평점 */
|
||||
.rating-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
margin: 20px 0;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.rating-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
transition: all 0.3s ease;
|
||||
min-width: 60px;
|
||||
background: #fafafa;
|
||||
border: 2px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.rating-item:hover {
|
||||
background: var(--survey-primary-light);
|
||||
border-color: var(--survey-primary);
|
||||
}
|
||||
|
||||
.rating-item.selected {
|
||||
background: var(--survey-primary);
|
||||
color: white;
|
||||
border-color: var(--survey-primary);
|
||||
}
|
||||
|
||||
.rating-number {
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.rating-label {
|
||||
font-size: 0.8em;
|
||||
text-align: center;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
/* 날짜 입력 */
|
||||
.form-date {
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
padding: 15px 20px;
|
||||
border: 2px solid #e0e0e0;
|
||||
border-radius: 10px;
|
||||
font-size: 1em;
|
||||
background: #fafafa;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.form-date:focus {
|
||||
outline: none;
|
||||
border-color: var(--survey-primary);
|
||||
background: white;
|
||||
box-shadow: 0 0 0 3px var(--survey-primary-light);
|
||||
}
|
||||
|
||||
/* 제출 버튼 */
|
||||
.submit-container {
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
padding-top: 30px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
background: linear-gradient(135deg, var(--survey-primary) 0%, var(--survey-primary-dark) 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 18px 50px;
|
||||
font-size: 1.1em;
|
||||
font-weight: 600;
|
||||
border-radius: 50px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.submit-btn:hover:not(:disabled) {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.submit-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
background: #ccc;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.submit-btn .loading-spinner {
|
||||
display: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid rgba(255,255,255,0.3);
|
||||
border-top: 2px solid white;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
.submit-btn.loading .loading-spinner {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 확인 모달 */
|
||||
.confirm-modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1050;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.confirm-modal.show {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.modal-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
animation: fadeIn 0.3s ease;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 90%;
|
||||
max-width: 500px;
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
||||
animation: slideUp 0.4s ease-out;
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 25px;
|
||||
font-size: 2rem;
|
||||
color: #aaa;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
line-height: 1;
|
||||
z-index: 1;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.modal-close:hover {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
padding: 30px 30px 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.modal-header h3 {
|
||||
font-size: 1.5em;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.modal-body p {
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.modal-warning {
|
||||
color: #e74c3c;
|
||||
font-size: 0.9em;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
padding: 20px 30px 30px;
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.btn-cancel {
|
||||
padding: 12px 24px;
|
||||
background: #f8f9fa;
|
||||
color: #666;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.btn-cancel:hover {
|
||||
background: #e9ecef;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.btn-confirm {
|
||||
padding: 12px 24px;
|
||||
background: var(--survey-primary);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.btn-confirm:hover {
|
||||
background: var(--survey-primary-dark);
|
||||
}
|
||||
|
||||
/* 반응형 */
|
||||
@media (max-width: 768px) {
|
||||
.survey-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.survey-form {
|
||||
padding: 20px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.survey-title {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.survey-info {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.question-input {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.question-description {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.rating-container {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.rating-item {
|
||||
min-width: 50px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
width: 95%;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.modal-header,
|
||||
.modal-body,
|
||||
.modal-footer {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.btn-cancel,
|
||||
.btn-confirm {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
/* 애니메이션 */
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translate(-50%, -40%);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* 질문 애니메이션 지연 */
|
||||
.question-container:nth-child(1) { animation-delay: 0.1s; }
|
||||
.question-container:nth-child(2) { animation-delay: 0.2s; }
|
||||
.question-container:nth-child(3) { animation-delay: 0.3s; }
|
||||
.question-container:nth-child(4) { animation-delay: 0.4s; }
|
||||
.question-container:nth-child(5) { animation-delay: 0.5s; }
|
||||
|
||||
/* 스크롤 하이라이트 효과 */
|
||||
.question-container.highlight {
|
||||
transform: scale(1.02);
|
||||
box-shadow: 0 5px 20px rgba(170, 32, 255, 0.1);
|
||||
border-color: var(--survey-primary-light);
|
||||
}
|
||||
|
||||
/* 유효성 검사 오류 스타일 */
|
||||
.form-input.error,
|
||||
.form-select.error {
|
||||
border-color: #e74c3c;
|
||||
background: #fdf2f2;
|
||||
}
|
||||
|
||||
.form-input.error:focus,
|
||||
.form-select.error:focus {
|
||||
box-shadow: 0 0 0 3px rgba(231, 76, 60, 0.1);
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: #e74c3c;
|
||||
font-size: 0.9em;
|
||||
margin-top: 8px;
|
||||
margin-left: 45px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.error-message.show {
|
||||
display: block;
|
||||
animation: fadeInUp 0.3s ease;
|
||||
}
|
||||
@@ -0,0 +1,507 @@
|
||||
function initSurveyFormModule(moduleId) {
|
||||
const moduleElement = document.getElementById(moduleId);
|
||||
if (!moduleElement || moduleElement.classList.contains('initialized')) return;
|
||||
|
||||
// 설문 데이터 가져오기
|
||||
const surveyData = JSON.parse(moduleElement.dataset.survey || '{}');
|
||||
const { survey, questions, theme_color } = surveyData;
|
||||
|
||||
// DOM 요소들
|
||||
const questionsContainer = moduleElement.querySelector('#questionsContainer');
|
||||
const progressFill = moduleElement.querySelector('#progressFill');
|
||||
const progressText = moduleElement.querySelector('#progressText');
|
||||
const currentQuestionSpan = moduleElement.querySelector('#currentQuestion');
|
||||
const submitBtn = moduleElement.querySelector('#submitBtn');
|
||||
const surveyForm = moduleElement.querySelector('#surveyForm');
|
||||
const confirmModal = moduleElement.querySelector('.confirm-modal');
|
||||
const modalOverlay = confirmModal.querySelector('.modal-overlay');
|
||||
const modalClose = confirmModal.querySelector('.modal-close');
|
||||
const btnCancel = confirmModal.querySelector('.btn-cancel');
|
||||
const btnConfirm = confirmModal.querySelector('.btn-confirm');
|
||||
|
||||
// CSS 변수 설정
|
||||
if (theme_color) {
|
||||
moduleElement.style.setProperty('--survey-primary', theme_color);
|
||||
moduleElement.style.setProperty('--survey-primary-light', theme_color + '20');
|
||||
moduleElement.style.setProperty('--survey-primary-dark', theme_color + 'CC');
|
||||
}
|
||||
|
||||
// 질문 렌더링
|
||||
function renderQuestions() {
|
||||
if (!questions || !questions.length) return;
|
||||
|
||||
const questionsHTML = questions.map((question, index) => {
|
||||
const fieldName = `answer_${question.sq_id}`;
|
||||
const required = question.sq_required ? 'required' : '';
|
||||
const requiredMark = question.sq_required ? '<span class="question-required">*</span>' : '';
|
||||
|
||||
let inputHTML = '';
|
||||
|
||||
switch (question.sq_type) {
|
||||
case 'text':
|
||||
inputHTML = `
|
||||
<input type="text"
|
||||
name="${fieldName}"
|
||||
class="form-input"
|
||||
placeholder="답변을 입력해주세요"
|
||||
${required}>
|
||||
`;
|
||||
break;
|
||||
|
||||
case 'textarea':
|
||||
inputHTML = `
|
||||
<textarea name="${fieldName}"
|
||||
class="form-input form-textarea"
|
||||
placeholder="자세한 답변을 입력해주세요"
|
||||
${required}></textarea>
|
||||
`;
|
||||
break;
|
||||
|
||||
case 'radio':
|
||||
if (question.sq_options && Array.isArray(question.sq_options)) {
|
||||
inputHTML = `
|
||||
<div class="radio-group">
|
||||
${question.sq_options.map((option, optionIndex) => `
|
||||
<div class="radio-item">
|
||||
<input type="radio"
|
||||
name="${fieldName}"
|
||||
value="${escapeHtml(option)}"
|
||||
id="${fieldName}_${optionIndex}"
|
||||
${required}>
|
||||
<label for="${fieldName}_${optionIndex}">
|
||||
${escapeHtml(option)}
|
||||
</label>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'checkbox':
|
||||
if (question.sq_options && Array.isArray(question.sq_options)) {
|
||||
inputHTML = `
|
||||
<div class="checkbox-group">
|
||||
${question.sq_options.map((option, optionIndex) => `
|
||||
<div class="checkbox-item">
|
||||
<input type="checkbox"
|
||||
name="${fieldName}[]"
|
||||
value="${escapeHtml(option)}"
|
||||
id="${fieldName}_${optionIndex}">
|
||||
<label for="${fieldName}_${optionIndex}">
|
||||
${escapeHtml(option)}
|
||||
</label>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'select':
|
||||
if (question.sq_options && Array.isArray(question.sq_options)) {
|
||||
inputHTML = `
|
||||
<select name="${fieldName}" class="form-select" ${required}>
|
||||
<option value="">선택해주세요</option>
|
||||
${question.sq_options.map(option => `
|
||||
<option value="${escapeHtml(option)}">
|
||||
${escapeHtml(option)}
|
||||
</option>
|
||||
`).join('')}
|
||||
</select>
|
||||
`;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'rating':
|
||||
const scale = question.sq_options?.scale || 5;
|
||||
const labels = question.sq_options?.labels || [];
|
||||
inputHTML = `
|
||||
<div class="rating-container">
|
||||
${Array.from({ length: scale }, (_, i) => i + 1).map(value => `
|
||||
<div class="rating-item" data-value="${value}">
|
||||
<input type="radio"
|
||||
name="${fieldName}"
|
||||
value="${value}"
|
||||
id="${fieldName}_${value}"
|
||||
style="display: none;"
|
||||
${required}>
|
||||
<div class="rating-number">${value}</div>
|
||||
${labels[value - 1] ? `<div class="rating-label">${escapeHtml(labels[value - 1])}</div>` : ''}
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
`;
|
||||
break;
|
||||
|
||||
case 'date':
|
||||
inputHTML = `
|
||||
<input type="date"
|
||||
name="${fieldName}"
|
||||
class="form-date"
|
||||
${required}>
|
||||
`;
|
||||
break;
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="question-container" data-question="${index + 1}">
|
||||
<div class="question-header">
|
||||
<span class="question-number">${index + 1}</span>
|
||||
<h3 class="question-title">
|
||||
${escapeHtml(question.sq_title)}
|
||||
${requiredMark}
|
||||
</h3>
|
||||
|
||||
${question.sq_description ? `
|
||||
<p class="question-description">${escapeHtml(question.sq_description)}</p>
|
||||
` : ''}
|
||||
</div>
|
||||
|
||||
<div class="question-input">
|
||||
${inputHTML}
|
||||
<div class="error-message" id="error_${question.sq_id}"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
questionsContainer.innerHTML = questionsHTML;
|
||||
}
|
||||
|
||||
// HTML 이스케이프 함수
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
// 진행률 업데이트
|
||||
function updateProgress() {
|
||||
let answeredQuestions = 0;
|
||||
const totalQuestions = questions.length;
|
||||
|
||||
questions.forEach((question, index) => {
|
||||
const questionContainer = questionsContainer.children[index];
|
||||
if (!questionContainer) return;
|
||||
|
||||
const inputs = questionContainer.querySelectorAll('input, textarea, select');
|
||||
let hasAnswer = false;
|
||||
|
||||
inputs.forEach(input => {
|
||||
if (input.type === 'radio' || input.type === 'checkbox') {
|
||||
if (input.checked) hasAnswer = true;
|
||||
} else if (input.value.trim() !== '') {
|
||||
hasAnswer = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (hasAnswer) {
|
||||
answeredQuestions++;
|
||||
}
|
||||
});
|
||||
|
||||
const progress = Math.round((answeredQuestions / totalQuestions) * 100);
|
||||
progressFill.style.width = progress + '%';
|
||||
progressText.textContent = progress + '%';
|
||||
currentQuestionSpan.textContent = answeredQuestions;
|
||||
|
||||
// 모든 필수 질문이 답변되었는지 확인
|
||||
const requiredQuestions = questionsContainer.querySelectorAll('input[required], textarea[required], select[required]');
|
||||
let allRequiredAnswered = true;
|
||||
|
||||
requiredQuestions.forEach(input => {
|
||||
if (input.type === 'radio') {
|
||||
const radioGroup = questionsContainer.querySelectorAll(`input[name="${input.name}"]`);
|
||||
let radioChecked = false;
|
||||
radioGroup.forEach(radio => {
|
||||
if (radio.checked) radioChecked = true;
|
||||
});
|
||||
if (!radioChecked) allRequiredAnswered = false;
|
||||
} else if (input.type === 'checkbox') {
|
||||
const checkboxGroup = questionsContainer.querySelectorAll(`input[name="${input.name}"]`);
|
||||
let checkboxChecked = false;
|
||||
checkboxGroup.forEach(checkbox => {
|
||||
if (checkbox.checked) checkboxChecked = true;
|
||||
});
|
||||
if (!checkboxChecked) allRequiredAnswered = false;
|
||||
} else if (input.value.trim() === '') {
|
||||
allRequiredAnswered = false;
|
||||
}
|
||||
});
|
||||
|
||||
submitBtn.disabled = !allRequiredAnswered;
|
||||
}
|
||||
|
||||
// 이벤트 리스너 바인딩
|
||||
function bindEvents() {
|
||||
// 라디오/체크박스 스타일링
|
||||
questionsContainer.addEventListener('click', (e) => {
|
||||
const radioItem = e.target.closest('.radio-item');
|
||||
const checkboxItem = e.target.closest('.checkbox-item');
|
||||
|
||||
if (radioItem) {
|
||||
const input = radioItem.querySelector('input');
|
||||
const groupName = input.name;
|
||||
|
||||
// 라디오 버튼 그룹의 다른 선택 해제
|
||||
questionsContainer.querySelectorAll(`input[name="${groupName}"]`).forEach(radio => {
|
||||
radio.closest('.radio-item').classList.remove('selected');
|
||||
});
|
||||
|
||||
input.checked = true;
|
||||
radioItem.classList.add('selected');
|
||||
updateProgress();
|
||||
}
|
||||
|
||||
if (checkboxItem) {
|
||||
const input = checkboxItem.querySelector('input');
|
||||
input.checked = !input.checked;
|
||||
checkboxItem.classList.toggle('selected', input.checked);
|
||||
updateProgress();
|
||||
}
|
||||
});
|
||||
|
||||
// 평점 클릭 이벤트
|
||||
questionsContainer.addEventListener('click', (e) => {
|
||||
const ratingItem = e.target.closest('.rating-item');
|
||||
if (ratingItem) {
|
||||
const value = ratingItem.dataset.value;
|
||||
const input = ratingItem.querySelector('input');
|
||||
const container = ratingItem.closest('.rating-container');
|
||||
|
||||
// 같은 그룹의 다른 평점 해제
|
||||
container.querySelectorAll('.rating-item').forEach(item => {
|
||||
item.classList.remove('selected');
|
||||
});
|
||||
|
||||
// 현재 평점 선택
|
||||
ratingItem.classList.add('selected');
|
||||
input.checked = true;
|
||||
|
||||
updateProgress();
|
||||
}
|
||||
});
|
||||
|
||||
// 일반 입력 필드 이벤트
|
||||
questionsContainer.addEventListener('input', updateProgress);
|
||||
questionsContainer.addEventListener('change', updateProgress);
|
||||
|
||||
// 폼 제출 이벤트
|
||||
surveyForm.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (submitBtn.disabled) {
|
||||
showNotification('모든 필수 항목을 입력해주세요.', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// 확인 모달 표시
|
||||
showConfirmModal();
|
||||
});
|
||||
|
||||
// 모달 이벤트
|
||||
modalClose.addEventListener('click', hideConfirmModal);
|
||||
btnCancel.addEventListener('click', hideConfirmModal);
|
||||
modalOverlay.addEventListener('click', hideConfirmModal);
|
||||
|
||||
btnConfirm.addEventListener('click', () => {
|
||||
hideConfirmModal();
|
||||
submitSurvey();
|
||||
});
|
||||
|
||||
// ESC 키로 모달 닫기
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' && confirmModal.classList.contains('show')) {
|
||||
hideConfirmModal();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 확인 모달 표시
|
||||
function showConfirmModal() {
|
||||
confirmModal.classList.add('show');
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
// 확인 모달 숨기기
|
||||
function hideConfirmModal() {
|
||||
confirmModal.classList.remove('show');
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
// 설문 제출
|
||||
function submitSurvey() {
|
||||
submitBtn.classList.add('loading');
|
||||
submitBtn.innerHTML = '<div class="loading-spinner"></div> 제출 중...';
|
||||
submitBtn.disabled = true;
|
||||
|
||||
const formData = new FormData(surveyForm);
|
||||
formData.append('ajax', '1');
|
||||
|
||||
fetch(surveyForm.action, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showNotification('설문이 성공적으로 제출되었습니다!', 'success');
|
||||
setTimeout(() => {
|
||||
window.location.href = data.redirect_url;
|
||||
}, 1500);
|
||||
} else {
|
||||
showNotification(data.errors ? data.errors.join('\n') : '제출 중 오류가 발생했습니다.', 'error');
|
||||
submitBtn.classList.remove('loading');
|
||||
submitBtn.innerHTML = '<i class="fa fa-paper-plane"></i> 설문 제출하기';
|
||||
submitBtn.disabled = false;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
showNotification('제출 중 오류가 발생했습니다.', 'error');
|
||||
submitBtn.classList.remove('loading');
|
||||
submitBtn.innerHTML = '<i class="fa fa-paper-plane"></i> 설문 제출하기';
|
||||
submitBtn.disabled = false;
|
||||
});
|
||||
}
|
||||
|
||||
// 알림 표시
|
||||
function showNotification(message, type = 'info') {
|
||||
const notification = document.createElement('div');
|
||||
notification.className = `survey-notification survey-notification-${type}`;
|
||||
notification.innerHTML = `
|
||||
<div class="notification-content">
|
||||
<i class="fa fa-${type === 'success' ? 'check-circle' : type === 'error' ? 'exclamation-circle' : 'info-circle'}"></i>
|
||||
<span>${message}</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 스타일 추가
|
||||
notification.style.cssText = `
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background: ${type === 'success' ? '#28a745' : type === 'error' ? '#dc3545' : '#17a2b8'};
|
||||
color: white;
|
||||
padding: 15px 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
|
||||
z-index: 1060;
|
||||
animation: slideInRight 0.3s ease;
|
||||
max-width: 300px;
|
||||
word-wrap: break-word;
|
||||
`;
|
||||
|
||||
document.body.appendChild(notification);
|
||||
|
||||
setTimeout(() => {
|
||||
notification.style.animation = 'slideOutRight 0.3s ease';
|
||||
setTimeout(() => {
|
||||
notification.remove();
|
||||
}, 300);
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// 스크롤 시 질문 하이라이트
|
||||
function initScrollHighlight() {
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
// 모든 하이라이트 제거
|
||||
questionsContainer.querySelectorAll('.question-container').forEach(q => {
|
||||
q.classList.remove('highlight');
|
||||
});
|
||||
// 현재 질문 하이라이트
|
||||
entry.target.classList.add('highlight');
|
||||
}
|
||||
});
|
||||
}, {
|
||||
threshold: 0.5,
|
||||
rootMargin: '-20% 0px -20% 0px'
|
||||
});
|
||||
|
||||
questionsContainer.querySelectorAll('.question-container').forEach(question => {
|
||||
observer.observe(question);
|
||||
});
|
||||
}
|
||||
|
||||
// 질문 애니메이션 초기화
|
||||
function initQuestionAnimations() {
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.style.opacity = '1';
|
||||
entry.target.style.animationPlayState = 'running';
|
||||
}
|
||||
});
|
||||
}, {
|
||||
threshold: 0.1,
|
||||
rootMargin: '0px 0px -50px 0px'
|
||||
});
|
||||
|
||||
questionsContainer.querySelectorAll('.question-container').forEach(question => {
|
||||
question.style.opacity = '0';
|
||||
question.style.animationPlayState = 'paused';
|
||||
observer.observe(question);
|
||||
});
|
||||
}
|
||||
|
||||
// 초기화
|
||||
function init() {
|
||||
renderQuestions();
|
||||
bindEvents();
|
||||
updateProgress();
|
||||
initScrollHighlight();
|
||||
initQuestionAnimations();
|
||||
|
||||
moduleElement.classList.add('initialized');
|
||||
}
|
||||
|
||||
// 모듈 초기화 실행
|
||||
init();
|
||||
}
|
||||
|
||||
// 추가 CSS 애니메이션 (JavaScript로 동적 추가)
|
||||
const additionalStyles = `
|
||||
@keyframes slideInRight {
|
||||
from {
|
||||
transform: translateX(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideOutRight {
|
||||
from {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
transform: translateX(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.survey-notification .notification-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.survey-notification i {
|
||||
font-size: 1.2em;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
// 스타일 추가 (한 번만)
|
||||
if (!document.getElementById('survey-form-module-styles')) {
|
||||
const styleSheet = document.createElement('style');
|
||||
styleSheet.id = 'survey-form-module-styles';
|
||||
styleSheet.textContent = additionalStyles;
|
||||
document.head.appendChild(styleSheet);
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
<?php
|
||||
if (!defined('_GNUBOARD_')) exit;
|
||||
|
||||
// 설문 관리 라이브러리 로드
|
||||
$survey_lib_path = G5_ADMIN_PATH.'/survey_manage/lib/survey.lib.php';
|
||||
if (!file_exists($survey_lib_path)) {
|
||||
echo '<div class="alert alert-danger">설문 라이브러리 파일이 없습니다.</div>';
|
||||
return;
|
||||
}
|
||||
include_once($survey_lib_path);
|
||||
|
||||
// 모듈 설정에서 설문 ID 가져오기 (rb.custom 모듈 방식)
|
||||
$sv_id = 0;
|
||||
|
||||
// 1. 모듈 설정에서 sv_id 확인 (rb_module 테이블의 설정값)
|
||||
if (isset($row_mod['md_custom_survey_key']) && $row_mod['md_custom_survey_key']) {
|
||||
$sv_id = (int)$row_mod['md_custom_survey_key'];
|
||||
}
|
||||
|
||||
// 2. URL 파라미터에서 sv_id 확인 (우선순위 높음)
|
||||
if (isset($_GET['sv_id']) && $_GET['sv_id']) {
|
||||
$sv_id = (int)$_GET['sv_id'];
|
||||
}
|
||||
else if (isset($_POST['sv_id']) && $_POST['sv_id']) {
|
||||
$sv_id = (int)$_POST['sv_id'];
|
||||
}
|
||||
|
||||
|
||||
// sv_id가 없으면 설문 선택 인터페이스 표시
|
||||
if (!$sv_id && $sv_id == 0) {
|
||||
echo '<div class="survey-form-module">';
|
||||
echo '<div class="survey-container">';
|
||||
echo '<div class="survey-header">';
|
||||
echo '<div class="container">';
|
||||
echo '<h3>설문을 선택해주세요</h3>';
|
||||
echo '<p>모듈 설정에서 설문을 선택하거나, URL에 ?sv_id=설문번호를 추가하여 특정 설문을 표시할 수 있습니다.</p>';
|
||||
|
||||
// 모든 설문 목록 표시 (관리자용)
|
||||
if ($is_admin) {
|
||||
$all_surveys = sql_query("SELECT sv_id, sv_title, sv_status, sv_start_date, sv_end_date FROM survey_master ORDER BY sv_created_at DESC LIMIT 10");
|
||||
|
||||
if (sql_num_rows($all_surveys) > 0) {
|
||||
echo '<div class="survey-list admin-survey-list">';
|
||||
echo '<h4>설문 목록 (관리자 전용):</h4>';
|
||||
echo '<div class="survey-grid">';
|
||||
while ($survey_item = sql_fetch_array($all_surveys)) {
|
||||
$status_class = '';
|
||||
$status_text = '';
|
||||
switch($survey_item['sv_status']) {
|
||||
case 'active': $status_class = 'status-active'; $status_text = '진행중'; break;
|
||||
case 'draft': $status_class = 'status-draft'; $status_text = '임시저장'; break;
|
||||
case 'closed': $status_class = 'status-closed'; $status_text = '종료'; break;
|
||||
default: $status_class = 'status-default'; $status_text = $survey_item['sv_status']; break;
|
||||
}
|
||||
|
||||
echo '<div class="survey-card">';
|
||||
echo '<div class="survey-card-header">';
|
||||
echo '<h5><a href="?sv_id='.$survey_item['sv_id'].'">'.$survey_item['sv_title'].'</a></h5>';
|
||||
echo '<span class="survey-status '.$status_class.'">'.$status_text.'</span>';
|
||||
echo '</div>';
|
||||
echo '<div class="survey-card-body">';
|
||||
echo '<p>기간: '.date('Y-m-d', strtotime($survey_item['sv_start_date'])).' ~ '.date('Y-m-d', strtotime($survey_item['sv_end_date'])).'</p>';
|
||||
echo '<a href="?sv_id='.$survey_item['sv_id'].'" class="btn-view-survey">설문 보기</a>';
|
||||
echo '</div>';
|
||||
echo '</div>';
|
||||
}
|
||||
echo '</div>';
|
||||
echo '</div>';
|
||||
}
|
||||
} else {
|
||||
// 일반 사용자용 - 활성화된 설문만 표시
|
||||
$active_surveys = sql_query("SELECT sv_id, sv_title, sv_description FROM survey_master WHERE sv_status = 'active' AND sv_start_date <= NOW() AND sv_end_date >= NOW() ORDER BY sv_created_at DESC LIMIT 5");
|
||||
|
||||
if (sql_num_rows($active_surveys) > 0) {
|
||||
echo '<div class="survey-list">';
|
||||
echo '<h4>현재 진행 중인 설문:</h4>';
|
||||
echo '<div class="survey-grid">';
|
||||
while ($survey_item = sql_fetch_array($active_surveys)) {
|
||||
echo '<div class="survey-card">';
|
||||
echo '<div class="survey-card-header">';
|
||||
echo '<h5><a href="?sv_id='.$survey_item['sv_id'].'">'.$survey_item['sv_title'].'</a></h5>';
|
||||
echo '</div>';
|
||||
if ($survey_item['sv_description']) {
|
||||
echo '<div class="survey-card-body">';
|
||||
echo '<p>'.htmlspecialchars(mb_substr($survey_item['sv_description'], 0, 100)).'...</p>';
|
||||
echo '<a href="?sv_id='.$survey_item['sv_id'].'" class="btn-view-survey">참여하기</a>';
|
||||
echo '</div>';
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
echo '</div>';
|
||||
echo '</div>';
|
||||
} else {
|
||||
echo '<div class="no-surveys">';
|
||||
echo '<i class="fa fa-clipboard" style="font-size: 3em; color: #ddd; margin-bottom: 15px;"></i>';
|
||||
echo '<p>현재 진행 중인 설문이 없습니다.</p>';
|
||||
echo '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
echo '</div></div></div></div>';
|
||||
|
||||
// 기본 스타일 추가
|
||||
echo '<style>
|
||||
.survey-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 15px; margin-top: 15px; }
|
||||
.survey-card { border: 1px solid #ddd; border-radius: 8px; padding: 15px; background: white; }
|
||||
.survey-card-header { margin-bottom: 10px; }
|
||||
.survey-card-header h5 { margin: 0 0 5px 0; }
|
||||
.survey-card-header a { color: #333; text-decoration: none; }
|
||||
.survey-card-header a:hover { color: #AA20FF; }
|
||||
.survey-status { padding: 3px 8px; border-radius: 12px; font-size: 0.8em; }
|
||||
.status-active { background: #d4edda; color: #155724; }
|
||||
.status-draft { background: #fff3cd; color: #856404; }
|
||||
.status-closed { background: #f8d7da; color: #721c24; }
|
||||
.btn-view-survey { display: inline-block; padding: 8px 15px; background: #AA20FF; color: white; text-decoration: none; border-radius: 4px; font-size: 0.9em; }
|
||||
.btn-view-survey:hover { background: #8A1ACC; color: white; }
|
||||
.no-surveys { text-align: center; padding: 40px; color: #666; }
|
||||
</style>';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 함수 존재 확인
|
||||
if (!function_exists('validate_survey_access')) {
|
||||
echo '<div class="alert alert-danger">설문 검증 함수가 정의되지 않았습니다.</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// 설문 유효성 검사
|
||||
$validation = validate_survey_access($sv_id, $member['mb_id'], $_SERVER['REMOTE_ADDR']);
|
||||
if (!$validation['success']) {
|
||||
echo '<div class="alert alert-warning">'.$validation['message'].'</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
$survey = $validation['survey'];
|
||||
$questions = get_survey_questions($sv_id);
|
||||
|
||||
if (empty($questions)) {
|
||||
echo '<div class="alert alert-info">이 설문에는 아직 질문이 등록되지 않았습니다.</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// 설문 테마 색상
|
||||
$theme_color = $survey['sv_theme_color'] ?: '#AA20FF';
|
||||
|
||||
// 설문 데이터를 JSON으로 준비
|
||||
$survey_data = array(
|
||||
'survey' => $survey,
|
||||
'questions' => $questions,
|
||||
'theme_color' => $theme_color,
|
||||
'total_responses' => get_survey_response_count($sv_id, 'completed')
|
||||
);
|
||||
|
||||
$survey_json = json_encode($survey_data, JSON_UNESCAPED_UNICODE);
|
||||
|
||||
// CSS와 JS 파일의 버전을 파일 수정 시간으로 자동 갱신
|
||||
$module_css_path = G5_THEME_PATH.'/rb.custom/survey_form/module.css';
|
||||
$module_js_path = G5_THEME_PATH.'/rb.custom/survey_form/module.js';
|
||||
$module_css_ver = file_exists($module_css_path) ? filemtime($module_css_path) : G5_CSS_VER;
|
||||
$module_js_ver = file_exists($module_js_path) ? filemtime($module_js_path) : G5_JS_VER;
|
||||
// 이 모듈만의 고유 ID를 생성합니다.
|
||||
$module_id = 'survey_form_module_'.uniqid();
|
||||
?>
|
||||
|
||||
<!-- 모듈의 가장 바깥 요소에 고유 ID를 부여합니다. -->
|
||||
<div id="<?php echo $module_id; ?>" class="survey-form-module" data-survey='<?php echo htmlspecialchars($survey_json, ENT_QUOTES, 'UTF-8'); ?>'>
|
||||
<div class="survey-container">
|
||||
<!-- 설문 헤더 -->
|
||||
<div class="survey-header">
|
||||
<div class="container">
|
||||
<h1 class="survey-title"><?php echo htmlspecialchars($survey['sv_title']); ?></h1>
|
||||
<?php if ($survey['sv_description']): ?>
|
||||
<p class="survey-description"><?php echo nl2br(htmlspecialchars($survey['sv_description'])); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="survey-info">
|
||||
<div class="survey-info-item">
|
||||
<i class="fa fa-clock"></i>
|
||||
<span>예상 소요시간: <span class="estimated-time"><?php echo count($questions); ?>분</span></span>
|
||||
</div>
|
||||
<div class="survey-info-item">
|
||||
<i class="fa fa-question-circle"></i>
|
||||
<span>총 <span class="total-questions"><?php echo count($questions); ?></span>개 질문</span>
|
||||
</div>
|
||||
<div class="survey-info-item">
|
||||
<i class="fa fa-users"></i>
|
||||
<span><span class="total-participants"><?php echo number_format(get_survey_response_count($sv_id, 'completed')); ?></span>명 참여</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 진행률 바 -->
|
||||
<div class="progress-container">
|
||||
<div class="container">
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" id="progressFill"></div>
|
||||
</div>
|
||||
<div class="progress-text">
|
||||
<span id="progressText">0%</span> 완료 (<span id="currentQuestion">0</span> / <span class="total-questions"><?php echo count($questions); ?></span>)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 설문 폼 -->
|
||||
<div class="survey-form-container">
|
||||
<div class="container">
|
||||
<form id="surveyForm" class="survey-form" method="post" action="<?php echo G5_THEME_URL; ?>/rb.custom/survey_form/survey_submit_page.php">
|
||||
<input type="hidden" name="sv_id" value="<?php echo $sv_id; ?>">
|
||||
|
||||
<div class="questions-container" id="questionsContainer">
|
||||
<!-- JavaScript로 질문들이 동적으로 생성됩니다 -->
|
||||
</div>
|
||||
|
||||
<div class="submit-container">
|
||||
<button type="submit" class="submit-btn" id="submitBtn" disabled>
|
||||
<i class="fa fa-paper-plane"></i> 설문 제출하기
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 확인 모달 -->
|
||||
<div class="confirm-modal">
|
||||
<div class="modal-overlay"></div>
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3>설문 제출 확인</h3>
|
||||
<button type="button" class="modal-close">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>설문을 제출하시겠습니까?</p>
|
||||
<p class="modal-warning">제출 후에는 수정할 수 없습니다.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn-cancel">취소</button>
|
||||
<button type="button" class="btn-confirm">제출하기</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 이 모듈에 필요한 CSS 파일을 불러옵니다. -->
|
||||
<link rel="stylesheet" href="<?php echo G5_THEME_URL; ?>/rb.custom/survey_form/module.css?ver=<?php echo $module_css_ver; ?>">
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const currentModuleId = '<?php echo $module_id; ?>';
|
||||
const initFunctionName = 'initSurveyFormModule';
|
||||
const scriptId = 'survey-form-module-script';
|
||||
|
||||
if (document.getElementById(scriptId)) {
|
||||
if (typeof window[initFunctionName] === 'function') {
|
||||
window[initFunctionName](currentModuleId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const script = document.createElement('script');
|
||||
script.id = scriptId;
|
||||
script.src = '<?php echo G5_THEME_URL; ?>/rb.custom/survey_form/module.js?ver=<?php echo $module_js_ver; ?>';
|
||||
script.async = true;
|
||||
|
||||
script.onload = () => {
|
||||
if (typeof window[initFunctionName] === 'function') {
|
||||
window[initFunctionName](currentModuleId);
|
||||
}
|
||||
};
|
||||
document.head.appendChild(script);
|
||||
})();
|
||||
</script>
|
||||
@@ -0,0 +1,563 @@
|
||||
<?php
|
||||
include_once('../../_common.php');
|
||||
|
||||
// 설문 관리 라이브러리 로드
|
||||
include_once(G5_ADMIN_PATH . '/survey_manage/lib/survey.lib.php');
|
||||
|
||||
$sv_id = isset($_GET['sv_id']) ? (int)$_GET['sv_id'] : 0;
|
||||
$sr_id = isset($_GET['sr_id']) ? (int)$_GET['sr_id'] : 0;
|
||||
|
||||
if (!$sv_id || !$sr_id) {
|
||||
alert('잘못된 접근입니다.', G5_URL);
|
||||
}
|
||||
|
||||
$survey = get_survey($sv_id);
|
||||
if (!$survey) {
|
||||
alert('존재하지 않는 설문입니다.', G5_URL);
|
||||
}
|
||||
|
||||
// 응답 확인
|
||||
$response = sql_fetch("SELECT * FROM survey_responses WHERE sr_id = '$sr_id' AND sv_id = '$sv_id'");
|
||||
if (!$response || $response['sr_status'] !== 'completed') {
|
||||
alert('완료되지 않은 응답입니다.', G5_URL);
|
||||
}
|
||||
|
||||
$g5['title'] = '설문 완료 - ' . $survey['sv_title'];
|
||||
include_once(G5_THEME_PATH . '/head.sub.php');
|
||||
|
||||
// 설문 테마 색상
|
||||
$theme_color = $survey['sv_theme_color'] ?: '#AA20FF';
|
||||
|
||||
// 응답 통계
|
||||
$total_responses = get_survey_response_count($sv_id, 'completed');
|
||||
?>
|
||||
|
||||
<style>
|
||||
/* 페이지 전용 스타일 */
|
||||
:root {
|
||||
--survey-primary: <?php echo $theme_color; ?>;
|
||||
--survey-primary-light: <?php echo $theme_color; ?>20;
|
||||
--survey-primary-dark: <?php echo $theme_color; ?>CC;
|
||||
}
|
||||
|
||||
body {
|
||||
background: linear-gradient(135deg, var(--survey-primary-light) 0%, #ffffff 100%);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.complete-page-container {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 100px 20px 20px;
|
||||
}
|
||||
|
||||
.complete-card {
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
padding: 60px 40px;
|
||||
text-align: center;
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
animation: slideInUp 0.8s ease;
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.complete-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 5px;
|
||||
background: linear-gradient(90deg, var(--survey-primary) 0%, var(--survey-primary-dark) 100%);
|
||||
}
|
||||
|
||||
.success-icon {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: var(--survey-primary);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 30px;
|
||||
animation: bounceIn 1s ease 0.3s both;
|
||||
}
|
||||
|
||||
.success-icon i {
|
||||
font-size: 3em;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.complete-title {
|
||||
font-size: 2.5em;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
margin-bottom: 20px;
|
||||
animation: fadeInUp 0.8s ease 0.5s both;
|
||||
}
|
||||
|
||||
.complete-message {
|
||||
font-size: 1.2em;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 30px;
|
||||
animation: fadeInUp 0.8s ease 0.7s both;
|
||||
}
|
||||
|
||||
.thank-message {
|
||||
background: var(--survey-primary-light);
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
margin: 30px 0;
|
||||
font-style: italic;
|
||||
color: var(--survey-primary-dark);
|
||||
animation: fadeInUp 0.8s ease 0.9s both;
|
||||
}
|
||||
|
||||
.stats-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 30px;
|
||||
margin: 40px 0;
|
||||
animation: fadeInUp 0.8s ease 1.1s both;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 2em;
|
||||
font-weight: bold;
|
||||
color: var(--survey-primary);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 0.9em;
|
||||
color: #666;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
justify-content: center;
|
||||
margin-top: 40px;
|
||||
animation: fadeInUp 0.8s ease 1.3s both;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 15px 30px;
|
||||
border-radius: 50px;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: 1em;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--survey-primary);
|
||||
color: white;
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: var(--survey-primary-dark);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-outline {
|
||||
background: transparent;
|
||||
color: var(--survey-primary);
|
||||
border: 2px solid var(--survey-primary);
|
||||
}
|
||||
|
||||
.btn-outline:hover {
|
||||
background: var(--survey-primary);
|
||||
color: white;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.social-share {
|
||||
margin-top: 30px;
|
||||
padding-top: 30px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
animation: fadeInUp 0.8s ease 1.5s both;
|
||||
}
|
||||
|
||||
.social-share h4 {
|
||||
color: #666;
|
||||
margin-bottom: 15px;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.social-buttons {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.social-btn {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.social-btn:hover {
|
||||
transform: scale(1.1);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.social-facebook {
|
||||
background: #3b5998;
|
||||
}
|
||||
|
||||
.social-twitter {
|
||||
background: #1da1f2;
|
||||
}
|
||||
|
||||
.social-kakao {
|
||||
background: #fee500;
|
||||
color: #3c1e1e;
|
||||
}
|
||||
|
||||
.social-line {
|
||||
background: #00c300;
|
||||
}
|
||||
|
||||
/* 파티클 효과 */
|
||||
.particles {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.particle {
|
||||
position: absolute;
|
||||
background: var(--survey-primary);
|
||||
border-radius: 50%;
|
||||
opacity: 0.6;
|
||||
animation: float 6s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.particle:nth-child(1) {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
left: 10%;
|
||||
animation-delay: 0s;
|
||||
}
|
||||
|
||||
.particle:nth-child(2) {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
left: 20%;
|
||||
animation-delay: 1s;
|
||||
}
|
||||
|
||||
.particle:nth-child(3) {
|
||||
width: 3px;
|
||||
height: 3px;
|
||||
left: 30%;
|
||||
animation-delay: 2s;
|
||||
}
|
||||
|
||||
.particle:nth-child(4) {
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
left: 40%;
|
||||
animation-delay: 3s;
|
||||
}
|
||||
|
||||
.particle:nth-child(5) {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
left: 50%;
|
||||
animation-delay: 4s;
|
||||
}
|
||||
|
||||
.particle:nth-child(6) {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
left: 60%;
|
||||
animation-delay: 5s;
|
||||
}
|
||||
|
||||
.particle:nth-child(7) {
|
||||
width: 3px;
|
||||
height: 3px;
|
||||
left: 70%;
|
||||
animation-delay: 0.5s;
|
||||
}
|
||||
|
||||
.particle:nth-child(8) {
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
left: 80%;
|
||||
animation-delay: 1.5s;
|
||||
}
|
||||
|
||||
.particle:nth-child(9) {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
left: 90%;
|
||||
animation-delay: 2.5s;
|
||||
}
|
||||
|
||||
/* 반응형 */
|
||||
@media (max-width: 768px) {
|
||||
.complete-page-container {
|
||||
padding: 80px 10px 20px;
|
||||
}
|
||||
|
||||
.complete-card {
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
.complete-title {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.stats-container {
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 100%;
|
||||
max-width: 250px;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
/* 애니메이션 */
|
||||
@keyframes slideInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(50px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bounceIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0.3);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
70% {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% {
|
||||
transform: translateY(100vh) rotate(0deg);
|
||||
opacity: 0;
|
||||
}
|
||||
10%, 90% {
|
||||
opacity: 0.6;
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-10px) rotate(180deg);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="complete-page-container">
|
||||
<div class="complete-card">
|
||||
<!-- 파티클 효과 -->
|
||||
<div class="particles">
|
||||
<div class="particle"></div>
|
||||
<div class="particle"></div>
|
||||
<div class="particle"></div>
|
||||
<div class="particle"></div>
|
||||
<div class="particle"></div>
|
||||
<div class="particle"></div>
|
||||
<div class="particle"></div>
|
||||
<div class="particle"></div>
|
||||
<div class="particle"></div>
|
||||
</div>
|
||||
|
||||
<!-- 성공 아이콘 -->
|
||||
<div class="success-icon">
|
||||
<i class="fa fa-check"></i>
|
||||
</div>
|
||||
|
||||
<!-- 완료 메시지 -->
|
||||
<h1 class="complete-title">설문 완료!</h1>
|
||||
<p class="complete-message">
|
||||
<strong><?php echo htmlspecialchars($survey['sv_title']); ?></strong><br>
|
||||
설문에 참여해 주셔서 감사합니다.
|
||||
</p>
|
||||
|
||||
<!-- 감사 메시지 -->
|
||||
<?php if ($survey['sv_thank_message']): ?>
|
||||
<div class="thank-message">
|
||||
<?php echo nl2br(htmlspecialchars($survey['sv_thank_message'])); ?>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="thank-message">
|
||||
소중한 의견을 주셔서 감사합니다.<br>
|
||||
더 나은 서비스를 위해 활용하겠습니다.
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- 통계 정보 -->
|
||||
<div class="stats-container">
|
||||
<div class="stat-item">
|
||||
<span class="stat-number"><?php echo number_format($total_responses); ?></span>
|
||||
<div class="stat-label">총 참여자 수</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-number"><?php echo $response['sr_id']; ?></span>
|
||||
<div class="stat-label">응답 번호</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 액션 버튼 -->
|
||||
<div class="action-buttons">
|
||||
<a href="<?php echo G5_URL; ?>" class="btn btn-primary">
|
||||
<i class="fa fa-home"></i> 홈으로 이동
|
||||
</a>
|
||||
<!-- <a href="survey_list_page.php" class="btn btn-outline">-->
|
||||
<!-- <i class="fa fa-list"></i> 다른 설문 보기-->
|
||||
<!-- </a>-->
|
||||
</div>
|
||||
|
||||
<!-- 소셜 공유 -->
|
||||
<div class="social-share">
|
||||
<h4>설문을 공유해보세요</h4>
|
||||
<div class="social-buttons">
|
||||
<a href="#" class="social-btn social-facebook" onclick="shareToFacebook()" title="페이스북 공유">
|
||||
<i class="fab fa-facebook-f"></i>
|
||||
</a>
|
||||
<a href="#" class="social-btn social-twitter" onclick="shareToTwitter()" title="트위터 공유">
|
||||
<i class="fab fa-twitter"></i>
|
||||
</a>
|
||||
<a href="#" class="social-btn social-kakao" onclick="shareToKakao()" title="카카오톡 공유">
|
||||
<i class="fab fa-comment"></i>
|
||||
</a>
|
||||
<a href="#" class="social-btn social-line" onclick="shareToLine()" title="라인 공유">
|
||||
<i class="fab fa-line"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 소셜 공유 함수들
|
||||
function shareToFacebook() {
|
||||
const url = encodeURIComponent(window.location.origin + '/theme/rd.lwd/survey_page.php?sv_id=<?php echo $sv_id; ?>');
|
||||
const title = encodeURIComponent('<?php echo addslashes($survey['sv_title']); ?>');
|
||||
window.open(`https://www.facebook.com/sharer/sharer.php?u=${url}`, '_blank', 'width=600,height=400');
|
||||
}
|
||||
|
||||
function shareToTwitter() {
|
||||
const url = encodeURIComponent(window.location.origin + '/theme/rd.lwd/survey_page.php?sv_id=<?php echo $sv_id; ?>');
|
||||
const text = encodeURIComponent('<?php echo addslashes($survey['sv_title']); ?> 설문에 참여해보세요!');
|
||||
window.open(`https://twitter.com/intent/tweet?url=${url}&text=${text}`, '_blank', 'width=600,height=400');
|
||||
}
|
||||
|
||||
function shareToKakao() {
|
||||
// 카카오톡 공유 (실제 구현시 카카오 SDK 필요)
|
||||
alert('카카오톡 공유 기능은 카카오 개발자 등록 후 사용 가능합니다.');
|
||||
}
|
||||
|
||||
function shareToLine() {
|
||||
const url = encodeURIComponent(window.location.origin + '/theme/rd.lwd/survey_page.php?sv_id=<?php echo $sv_id; ?>');
|
||||
const text = encodeURIComponent('<?php echo addslashes($survey['sv_title']); ?>');
|
||||
window.open(`https://social-plugins.line.me/lineit/share?url=${url}&text=${text}`, '_blank', 'width=600,height=400');
|
||||
}
|
||||
|
||||
// 페이지 로드 시 축하 효과
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// 3초 후 파티클 효과 시작
|
||||
setTimeout(() => {
|
||||
document.querySelector('.particles').style.display = 'block';
|
||||
}, 1000);
|
||||
|
||||
// 성공 사운드 효과 (선택사항)
|
||||
// playSuccessSound();
|
||||
});
|
||||
|
||||
// 성공 사운드 재생 함수 (선택사항)
|
||||
function playSuccessSound() {
|
||||
// Web Audio API를 사용한 간단한 성공 사운드
|
||||
try {
|
||||
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||||
const oscillator = audioContext.createOscillator();
|
||||
const gainNode = audioContext.createGain();
|
||||
|
||||
oscillator.connect(gainNode);
|
||||
gainNode.connect(audioContext.destination);
|
||||
|
||||
oscillator.frequency.setValueAtTime(523.25, audioContext.currentTime); // C5
|
||||
oscillator.frequency.setValueAtTime(659.25, audioContext.currentTime + 0.1); // E5
|
||||
oscillator.frequency.setValueAtTime(783.99, audioContext.currentTime + 0.2); // G5
|
||||
|
||||
gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
|
||||
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5);
|
||||
|
||||
oscillator.start(audioContext.currentTime);
|
||||
oscillator.stop(audioContext.currentTime + 0.5);
|
||||
} catch (e) {
|
||||
// 오디오 컨텍스트 생성 실패시 무시
|
||||
console.log('Audio context not supported');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php
|
||||
include_once(G5_THEME_PATH . '/tail.sub.php');
|
||||
?>
|
||||
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
//if (!defined('_GNUBOARD_')) exit;
|
||||
include_once('../../../../common.php');
|
||||
|
||||
// 설문 관리 라이브러리 로드
|
||||
include_once(G5_ADMIN_PATH.'/survey_manage/lib/survey.lib.php');
|
||||
|
||||
$sv_id = isset($_GET['sv_id']) ? (int)$_GET['sv_id'] : 0;
|
||||
|
||||
if (!$sv_id) {
|
||||
alert('잘못된 접근입니다.', G5_URL);
|
||||
}
|
||||
|
||||
// 설문 유효성 검사
|
||||
$validation = validate_survey_access($sv_id, $member['mb_id'], $_SERVER['REMOTE_ADDR']);
|
||||
if (!$validation['success']) {
|
||||
alert($validation['message']);
|
||||
return;
|
||||
}
|
||||
|
||||
$survey = $validation['survey'];
|
||||
$questions = get_survey_questions($sv_id);
|
||||
|
||||
if (empty($questions)) {
|
||||
alert('설문 질문이 없습니다.', G5_URL);
|
||||
}
|
||||
|
||||
$g5['title'] = $survey['sv_title'];
|
||||
include_once(G5_THEME_PATH.'/head.sub.php');
|
||||
|
||||
// 설문 테마 색상
|
||||
$theme_color = $survey['sv_theme_color'] ?: '#AA20FF';
|
||||
?>
|
||||
|
||||
<style>
|
||||
/* 페이지 전용 스타일 */
|
||||
:root {
|
||||
--survey-primary: <?php echo $theme_color; ?>;
|
||||
--survey-primary-light: <?php echo $theme_color; ?>20;
|
||||
--survey-primary-dark: <?php echo $theme_color; ?>CC;
|
||||
}
|
||||
|
||||
body {
|
||||
background: linear-gradient(135deg, var(--survey-primary-light) 0%, #ffffff 100%);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.survey-page-container {
|
||||
min-height: 100vh;
|
||||
padding-top: 100px; /* 헤더 높이만큼 */
|
||||
}
|
||||
|
||||
.survey-page-header {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
padding: 40px 20px;
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
max-width: 800px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.survey-page-header::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 5px;
|
||||
background: linear-gradient(90deg, var(--survey-primary) 0%, var(--survey-primary-dark) 100%);
|
||||
}
|
||||
|
||||
.survey-page-title {
|
||||
font-size: 2.5em;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
margin-bottom: 15px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.survey-page-description {
|
||||
font-size: 1.1em;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.survey-page-info {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 30px;
|
||||
margin-top: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.survey-page-info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: #888;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.survey-page-info-item i {
|
||||
color: var(--survey-primary);
|
||||
}
|
||||
|
||||
.survey-form-section {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px 80px;
|
||||
}
|
||||
|
||||
/* 반응형 */
|
||||
@media (max-width: 768px) {
|
||||
.survey-page-container {
|
||||
padding-top: 80px;
|
||||
}
|
||||
|
||||
.survey-page-title {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.survey-page-info {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.survey-form-section {
|
||||
padding: 0 10px 60px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="survey-page-container">
|
||||
<!-- 설문 헤더 -->
|
||||
<!-- <div class="survey-page-header">-->
|
||||
<!-- <h1 class="survey-page-title">--><?php //echo htmlspecialchars($survey['sv_title']); ?><!--</h1>-->
|
||||
<!-- --><?php //if ($survey['sv_description']): ?>
|
||||
<!-- <p class="survey-page-description">--><?php //echo nl2br(htmlspecialchars($survey['sv_description'])); ?><!--</p>-->
|
||||
<!-- --><?php //endif; ?>
|
||||
<!-- -->
|
||||
<!-- <div class="survey-page-info">-->
|
||||
<!-- <div class="survey-page-info-item">-->
|
||||
<!-- <i class="fa fa-clock"></i>-->
|
||||
<!-- <span>예상 소요시간: --><?php //echo count($questions); ?><!--분</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="survey-page-info-item">-->
|
||||
<!-- <i class="fa fa-question-circle"></i>-->
|
||||
<!-- <span>총 --><?php //echo count($questions); ?><!--개 질문</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="survey-page-info-item">-->
|
||||
<!-- <i class="fa fa-users"></i>-->
|
||||
<!-- <span>--><?php //echo number_format(get_survey_response_count($sv_id, 'completed')); ?><!--명 참여</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
|
||||
<!-- 설문 폼 섹션 (rb.custom 모듈 사용) -->
|
||||
<div class="survey-form-section">
|
||||
<?php
|
||||
// 설문 폼 커스텀 모듈 포함
|
||||
$_GET['sv_id'] = $sv_id; // 모듈에서 사용할 수 있도록 설정
|
||||
include_once(G5_THEME_PATH.'/rb.custom/survey_form/module.php');
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
include_once(G5_THEME_PATH.'/tail.sub.php');
|
||||
?>
|
||||
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
include_once('../../_common.php');
|
||||
|
||||
// 설문 관리 라이브러리 로드
|
||||
include_once(G5_ADMIN_PATH.'/survey_manage/lib/survey.lib.php');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
alert('잘못된 접근입니다.', G5_URL);
|
||||
}
|
||||
|
||||
$sv_id = isset($_POST['sv_id']) ? (int)$_POST['sv_id'] : 0;
|
||||
|
||||
if (!$sv_id) {
|
||||
alert('잘못된 접근입니다.', G5_URL);
|
||||
}
|
||||
|
||||
// 설문 유효성 검사
|
||||
$validation = validate_survey_access($sv_id, $member['mb_id'], $_SERVER['REMOTE_ADDR']);
|
||||
if (!$validation['success']) {
|
||||
alert($validation['message'], G5_URL);
|
||||
}
|
||||
|
||||
$survey = $validation['survey'];
|
||||
$questions = get_survey_questions($sv_id);
|
||||
|
||||
if (empty($questions)) {
|
||||
alert('설문 질문이 없습니다.', G5_URL);
|
||||
}
|
||||
|
||||
// 응답 시작
|
||||
$sr_id = start_survey_response(
|
||||
$sv_id,
|
||||
$member['mb_id'] ?: null,
|
||||
$_SERVER['REMOTE_ADDR'],
|
||||
$_SERVER['HTTP_USER_AGENT'] ?: '',
|
||||
session_id()
|
||||
);
|
||||
|
||||
if (!$sr_id) {
|
||||
alert('응답 저장 중 오류가 발생했습니다.', G5_URL);
|
||||
}
|
||||
|
||||
// 답변 저장
|
||||
$errors = array();
|
||||
foreach ($questions as $question) {
|
||||
$field_name = 'answer_' . $question['sq_id'];
|
||||
$value = isset($_POST[$field_name]) ? $_POST[$field_name] : '';
|
||||
|
||||
// 필수 질문 검증
|
||||
if ($question['sq_required']) {
|
||||
if (is_array($value)) {
|
||||
if (empty($value) || (count($value) == 1 && empty($value[0]))) {
|
||||
$errors[] = $question['sq_title'] . ' 항목은 필수입니다.';
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (empty(trim($value))) {
|
||||
$errors[] = $question['sq_title'] . ' 항목은 필수입니다.';
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 유효성 검사
|
||||
if ($question['sq_validation'] && !empty($value)) {
|
||||
$validation_rules = $question['sq_validation'];
|
||||
|
||||
if (isset($validation_rules['minLength']) && strlen($value) < $validation_rules['minLength']) {
|
||||
$errors[] = $question['sq_title'] . ' 항목은 최소 ' . $validation_rules['minLength'] . '자 이상 입력해주세요.';
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($validation_rules['maxLength']) && strlen($value) > $validation_rules['maxLength']) {
|
||||
$errors[] = $question['sq_title'] . ' 항목은 최대 ' . $validation_rules['maxLength'] . '자까지 입력 가능합니다.';
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($validation_rules['pattern'])) {
|
||||
$pattern = $validation_rules['pattern'];
|
||||
if ($pattern === 'email' && !filter_var($value, FILTER_VALIDATE_EMAIL)) {
|
||||
$errors[] = $question['sq_title'] . ' 항목에 올바른 이메일 주소를 입력해주세요.';
|
||||
continue;
|
||||
}
|
||||
if ($pattern === 'phone' && !preg_match('/^[0-9-+\s()]+$/', $value)) {
|
||||
$errors[] = $question['sq_title'] . ' 항목에 올바른 전화번호를 입력해주세요.';
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 답변 저장
|
||||
if (!empty($value) || $question['sq_required']) {
|
||||
save_survey_answer($sr_id, $question['sq_id'], $value);
|
||||
}
|
||||
}
|
||||
|
||||
// 오류가 있으면 응답 삭제하고 돌아가기
|
||||
if (!empty($errors)) {
|
||||
sql_query("DELETE FROM survey_responses WHERE sr_id = '$sr_id'");
|
||||
|
||||
// JSON 응답인지 확인
|
||||
if (isset($_POST['ajax']) && $_POST['ajax'] == '1') {
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(array(
|
||||
'success' => false,
|
||||
'errors' => $errors
|
||||
));
|
||||
exit;
|
||||
} else {
|
||||
alert(implode('\n', $errors), 'survey_page.php?sv_id=' . $sv_id);
|
||||
}
|
||||
}
|
||||
|
||||
// 응답 완료 처리
|
||||
complete_survey_response($sr_id);
|
||||
|
||||
// 통계 업데이트
|
||||
update_survey_statistics($sv_id);
|
||||
|
||||
// JSON 응답인지 확인
|
||||
if (isset($_POST['ajax']) && $_POST['ajax'] == '1') {
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(array(
|
||||
'success' => true,
|
||||
'redirect_url' => G5_THEME_PATH.'/rb.custom/survey_form/survey_complete_page.php?sv_id=' . $sv_id . '&sr_id=' . $sr_id
|
||||
));
|
||||
exit;
|
||||
} else {
|
||||
// 완료 페이지로 이동
|
||||
goto_url(G5_THEME_PATH.'/rb.custom/survey_form/survey_complete_page.php?sv_id=' . $sv_id . '&sr_id=' . $sr_id);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
if (!defined('_GNUBOARD_')) exit;
|
||||
|
||||
echo '<div style="padding: 20px; border: 2px solid #AA20FF; margin: 20px; background: #f9f9f9;">';
|
||||
echo '<h3>설문 폼 모듈 테스트</h3>';
|
||||
echo '<p>현재 시간: ' . date('Y-m-d H:i:s') . '</p>';
|
||||
|
||||
// 파라미터 확인
|
||||
echo '<h4>URL 파라미터:</h4>';
|
||||
echo '<ul>';
|
||||
foreach ($_GET as $key => $value) {
|
||||
echo '<li>' . htmlspecialchars($key) . ' = ' . htmlspecialchars($value) . '</li>';
|
||||
}
|
||||
echo '</ul>';
|
||||
|
||||
// 라이브러리 파일 확인
|
||||
$survey_lib_path = G5_ADMIN_PATH.'/survey_manage/lib/survey.lib.php';
|
||||
echo '<h4>파일 확인:</h4>';
|
||||
echo '<p>survey.lib.php: ' . (file_exists($survey_lib_path) ? '존재함' : '없음') . '</p>';
|
||||
|
||||
// 데이터베이스 테이블 확인
|
||||
$tables = ['survey_master', 'survey_questions', 'survey_responses'];
|
||||
echo '<h4>데이터베이스 테이블:</h4>';
|
||||
echo '<ul>';
|
||||
foreach ($tables as $table) {
|
||||
$result = sql_query("SHOW TABLES LIKE '$table'", false);
|
||||
echo '<li>' . $table . ': ' . ($result && sql_num_rows($result) > 0 ? '존재함' : '없음') . '</li>';
|
||||
}
|
||||
echo '</ul>';
|
||||
|
||||
// 설문 데이터 확인
|
||||
$sv_id = isset($_GET['sv_id']) ? (int)$_GET['sv_id'] : 0;
|
||||
if ($sv_id) {
|
||||
echo '<h4>설문 데이터 (sv_id: ' . $sv_id . '):</h4>';
|
||||
$survey = sql_fetch("SELECT * FROM survey_master WHERE sv_id = '$sv_id'");
|
||||
if ($survey) {
|
||||
echo '<p>제목: ' . htmlspecialchars($survey['sv_title']) . '</p>';
|
||||
echo '<p>상태: ' . $survey['sv_status'] . '</p>';
|
||||
} else {
|
||||
echo '<p>설문을 찾을 수 없습니다.</p>';
|
||||
}
|
||||
}
|
||||
|
||||
echo '</div>';
|
||||
?>
|
||||
Reference in New Issue
Block a user