first commit 2

This commit is contained in:
hmw1001
2026-06-11 18:47:38 +09:00
parent c768729ce6
commit 6f534e33a6
11095 changed files with 1595758 additions and 0 deletions
@@ -0,0 +1,270 @@
/*
* ==========================================================================
* 💡 베이스 모듈(Base Module) 전용 스타일시트
* ==========================================================================
*/
/* --- 1. 섹션 기본 스타일 --- */
.item-section {
width: 100%;
padding: 80px 0;
background-color: #ffffff;
}
/* --- 2. 섹션 헤더 (제목, 부제) --- */
.section-header {
text-align: center;
margin-bottom: 50px;
}
.section-header .subtitle {
font-size: 16px;
font-weight: 700;
color: #0056b3;
margin-bottom: 10px;
display: block;
}
.section-header h2 {
font-size: 36px;
font-weight: 900;
color: #25282B;
margin-bottom: 15px;
line-height: 1.4;
}
.section-header p {
font-size: 16px;
line-height: 1.7;
color: #666;
max-width: 600px;
margin: 0 auto;
}
/* --- 3. 아이템 카드 그리드 --- */
.item-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
}
/* --- 4. 개별 아이템 카드 --- */
.item-card {
background-color: #fff;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
transition: transform 0.3s ease, box-shadow 0.3s ease;
cursor: pointer;
}
.item-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
}
/* --- 5. 카드 내부 요소 --- */
.item-image {
width: 100%;
aspect-ratio: 4 / 3;
overflow: hidden;
}
.item-image.is-patent {
padding: 20px;
background-color: #f0f0f0;
}
.item-image img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.4s ease;
}
.item-card:hover .item-image img {
transform: scale(1.05);
}
.item-info {
padding: 25px;
}
.item-info h3 {
font-size: 1.2rem;
font-weight: 700;
margin-bottom: 10px;
color: #333;
}
.item-info p {
font-size: 0.95rem;
color: #666;
line-height: 1.6;
}
/* --- 9. 스크롤 애니메이션 (공용) --- */
.reveal-up, .reveal-fade {
opacity: 0;
transition: opacity 0.8s ease-out, transform 0.8s ease-out;
}
.reveal-up { transform: translateY(40px); }
.reveal-fade { transform: scale(0.95); }
.reveal-up.is-revealed, .reveal-fade.is-revealed {
opacity: 1;
transform: none;
}
/* --- 10. [모듈 전용] 이미지 팝업 모달 스타일 (개선) --- */
.image-modal {
display: none;
position: fixed;
z-index: 1050;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.85);
justify-content: center;
align-items: center;
padding: 40px 20px;
opacity: 0;
transition: opacity 0.3s ease;
}
.image-modal.is-active {
display: flex;
opacity: 1;
}
.modal-content {
position: relative;
background-color: #fff;
margin: auto;
padding: 0;
border-radius: 8px;
width: auto;
max-width: 100%;
box-shadow: 0 5px 15px rgba(0,0,0,.5);
animation: modal-slide-down 0.4s ease-out;
display: flex;
flex-direction: column;
}
@keyframes modal-slide-down {
from {
transform: translateY(-50px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
.modal-content .modal-image {
display: block;
max-width: 90vw;
max-height: 80vh;
object-fit: contain;
width: auto;
height: auto;
}
.modal-info {
padding: 20px 25px;
max-width: 800px; /* 텍스트 영역의 최대 너비는 제한 */
width: 100%;
}
.modal-info .modal-title {
font-size: 1.5rem;
font-weight: 700;
color: #333;
margin: 0 0 10px 0;
}
.modal-info .modal-desc {
font-size: 1rem;
color: #666;
line-height: 1.6;
margin: 0;
}
.close-btn {
color: #fff;
position: absolute;
top: 15px;
right: 25px;
font-size: 35px;
font-weight: bold;
cursor: pointer;
transition: color 0.2s;
text-shadow: 0 1px 3px rgba(0,0,0,0.5);
z-index: 10;
}
.close-btn:hover,
.close-btn:focus {
color: #ccc;
text-decoration: none;
}
/* --- 11. [개선] 가독성 향상을 위한 줄바꿈 처리 --- */
.section-header h2,
.section-header p,
.product-info-trend h3,
.product-info-trend p,
.item-info h3,
.item-info p {
word-break: keep-all;
overflow-wrap: break-word;
}
/* --- 12. [개선] 카드 설명 텍스트 잘림 방지 및 높이 고정 --- */
.product-info-trend p,
.item-info p {
height: 3.04rem;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
line-height: 1.6;
}
/* --- 13. [최종 수정] 모듈 스크롤바 완전 숨김 처리 --- */
/* 1. 기본 상태: 스크롤바를 완전히 투명하게 만듭니다. */
.image-modal {
/* Firefox: 스크롤바 색상을 양쪽 모두 투명하게 설정 */
scrollbar-color: transparent transparent;
scrollbar-width: thin;
transition: scrollbar-color 0.3s ease;
}
.image-modal::-webkit-scrollbar {
width: 10px;
}
.image-modal::-webkit-scrollbar-track {
background: transparent;
}
.image-modal::-webkit-scrollbar-thumb {
/* Webkit: 스크롤바 막대를 투명하게 */
background-color: transparent;
border-radius: 10px;
border: 3px solid transparent;
background-clip: padding-box;
transition: background-color 0.3s ease;
}
/* 2. :hover 상태: 마우스를 올리면 스크롤바가 나타납니다. */
.image-modal:hover {
/* Firefox: 스크롤바 색상을 보이게 변경 */
scrollbar-color: rgba(0, 0, 0, 0.4) transparent;
}
.image-modal:hover::-webkit-scrollbar-thumb {
/* Webkit: 스크롤바 막대 색상을 보이게 변경 */
background-color: rgba(0, 0, 0, 0.4);
}
@@ -0,0 +1,78 @@
// TODO: 'initBaseModule'을 고유한 함수 이름으로 변경하세요. (예: initNewsModule)
function initBaseModule(moduleId) {
const moduleElement = document.getElementById(moduleId);
if (!moduleElement || moduleElement.classList.contains('initialized')) {
return;
}
const section = moduleElement.querySelector('.base-section');
const itemGrid = moduleElement.querySelector('.item-grid');
const modal = moduleElement.querySelector('.image-modal');
if (!section || !itemGrid || !modal) {
return;
}
function renderGrid() {
const itemsJson = section.dataset.items;
let itemsData;
try {
itemsData = JSON.parse(itemsJson);
} catch (e) {
itemGrid.innerHTML = '<p>데이터를 불러오는 데 실패했습니다.</p>';
return;
}
if (!itemsData || itemsData.length === 0) {
itemGrid.innerHTML = '<p>표시할 항목이 없습니다.</p>';
return;
}
const itemCardsHTML = itemsData.map(item => `
<div class="item-card js-modal-trigger" data-title="${item.title}" data-desc="${item.description}" data-img="${item.image}">
<div class="item-image">
<img src="${item.image}" alt="${item.title}">
</div>
<div class="item-info">
<h3>${item.title}</h3>
<p>${item.description}</p>
</div>
</div>
`).join('');
itemGrid.innerHTML = itemCardsHTML;
}
function setupModalEvents() {
const modalImage = modal.querySelector('.modal-image');
const modalTitle = modal.querySelector('.modal-title');
const modalDesc = modal.querySelector('.modal-desc');
const closeBtn = modal.querySelector('.close-btn');
function closeModal() {
modal.classList.remove('is-active');
}
itemGrid.addEventListener('click', function(event) {
const card = event.target.closest('.js-modal-trigger');
if (card) {
modalImage.src = card.dataset.img;
modalTitle.textContent = card.dataset.title;
modalDesc.textContent = card.dataset.desc;
modal.classList.add('is-active');
}
});
closeBtn.addEventListener('click', closeModal);
modal.addEventListener('click', (e) => (e.target === modal) && closeModal());
document.addEventListener('keydown', (e) => (e.key === 'Escape' && modal.classList.contains('is-active')) && closeModal());
}
renderGrid();
setupModalEvents();
moduleElement.classList.add('initialized');
}
// TODO: 'initBaseModule'을 고유한 함수 이름으로 변경하세요.
window.initBaseModule = initBaseModule;
@@ -0,0 +1,102 @@
<?php
if (!defined('_GNUBOARD_')) exit;
// =================================================================================
// 💡 [사용법] 이 파일을 복사하여 새 모듈을 만들 때 아래 TODO 항목들을 수정하세요.
// =================================================================================
// TODO: 'board_name'을 실제 게시판 테이블명으로 변경하세요. (예: 'gallery', 'news' 등)
$bo_table_name = 'board_name';
// TODO: 가져올 게시물 수를 변경할 수 있습니다.
$limit = 3;
// --- 데이터 처리 ---
$module_data = array();
$sql = " SELECT wr_id, wr_subject, wr_content FROM {$g5['write_prefix']}{$bo_table_name} WHERE wr_is_comment = 0 ORDER BY wr_num, wr_reply LIMIT {$limit} ";
$result = sql_query($sql);
for ($i=0; $row=sql_fetch_array($result); $i++) {
$files = get_file($bo_table_name, $row['wr_id']);
$image_url = (isset($files[0]['path']) && isset($files[0]['file'])) ? $files[0]['path'].'/'.$files[0]['file'] : G5_THEME_URL.'/img/no_image.png';
$module_data[] = array(
'id' => $row['wr_id'],
'title' => get_text($row['wr_subject']),
'description' => get_text(cut_str(strip_tags($row['wr_content']), 100)),
'image' => $image_url
);
}
$module_json = json_encode($module_data, JSON_UNESCAPED_UNICODE);
// CSS와 JS 파일의 버전을 파일 수정 시간으로 자동 갱신
$module_css_path = G5_THEME_PATH.'/rb.custom/_base_module/module.css';
$module_js_path = G5_THEME_PATH.'/rb.custom/_base_module/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 = 'base_module_'.uniqid();
?>
<!-- 모듈의 가장 바깥 요소에 고유 ID를 부여합니다. -->
<div id="<?php echo $module_id; ?>" class="base-module">
<section class="item-section" data-items='<?php echo htmlspecialchars($module_json, ENT_QUOTES, 'UTF-8'); ?>'>
<div class="container">
<div class="section-header">
<!-- TODO: 모듈의 제목과 설명을 수정하세요. -->
<span class="subtitle">Module Subtitle</span>
<h2>모듈 제목</h2>
<p>이곳에 모듈에 대한 간단한 설명을 입력하세요.</p>
</div>
<div class="item-grid">
<!-- JS로 아이템 카드가 생성될 영역 -->
</div>
</div>
</section>
<!-- 모듈 내부에 모달 HTML을 포함시킵니다. -->
<div class="image-modal">
<div class="modal-content">
<span class="close-btn">&times;</span>
<img class="modal-image" src="" alt="이미지">
<div class="modal-info">
<h3 class="modal-title"></h3>
<p class="modal-desc"></p>
</div>
</div>
</div>
</div>
<!-- 이 모듈에 필요한 CSS 파일을 불러옵니다. -->
<link rel="stylesheet" href="<?php echo G5_THEME_URL; ?>/rb.custom/_item_module/module.css?ver=<?php echo $module_css_ver; ?>">
<script>
(function() {
const currentModuleId = '<?php echo $module_id; ?>';
// TODO: 'inititemModule'을 고유한 함수 이름으로 변경하세요. (예: initNewsModule)
const initFunctionName = 'inititemModule';
// TODO: 'item-module-script'를 고유한 스크립트 ID로 변경하세요. (예: news-module-script)
const scriptId = 'item-module-script';
if (document.getElementById(scriptId)) {
if (typeof window[initFunctionName] === 'function') {
window[initFunctionName](currentModuleId);
}
return;
}
const script = document.createElement('script');
script.id = scriptId;
// TODO: JS 파일 경로를 실제 모듈 경로로 수정하세요.
script.src = '<?php echo G5_THEME_URL; ?>/rb.custom/_base_module/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,33 @@
<?php
if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가
?>
<!-- 💡 [핵심] 사이트 전체에서 사용할 공용 문의하기 모달입니다. 기본적으로 숨겨져 있습니다. -->
<div id="global-contact-modal" class="global-contact-modal-container" style="display: none;">
<div class="modal-overlay-bg"></div>
<div class="modal-content-wrapper">
<button type="button" class="modal-close">&times;</button>
<div class="section-header">
<span class="subtitle">Contact Us</span>
<h2>공간의 가능성을 열어보세요.</h2>
<p>성진미도어의 전문가와 상담하고, 당신의 공간에 꼭 맞는 솔루션을 찾아보세요.</p>
</div>
<div class="contact-wrapper">
<div class="contact-info">
<h3>Information</h3>
<div class="info-item"><strong>본사 및 전시장</strong><p>충청남도 아산시 음봉면 월산로 128-130</p></div>
<div class="info-item"><strong>대표 연락처</strong><p>T. 041-532-0555 / H. 010-5434-4126</p></div>
<div class="info-item"><strong>청주 전시장</strong><p>T. 043-235-2352 / H. 010-2066-4126</p></div>
<div class="info-item"><strong>운영시간</strong><p>평일 09:00 - 18:00 (주말 및 공휴일 휴무)</p></div>
</div>
<form name="contactForm" id="contactForm" class="contact-form" method="post">
<h3>상담 신청</h3>
<div id="form-messages" class="form-message-area"></div>
<input type="text" name="contact_name" placeholder="이름" required>
<input type="tel" name="contact_hp" placeholder="연락처" required>
<input type="email" name="contact_email" placeholder="이메일" required>
<textarea name="contact_message" placeholder="문의 내용을 입력해주세요." rows="5" required></textarea>
<button type="submit" id="contact-submit-btn" class="cta-button">상담 신청하기</button>
</form>
</div>
</div>
</div>
@@ -0,0 +1,37 @@
/* 독립 문의하기 모듈 스타일 */
.contact-section-trigger { padding: 80px 0; text-align: center; background-color: #f8f9fa; }
.contact-section-trigger .cta-button { padding: 15px 30px; border-radius: 50px; font-weight: 700; }
.contact-modal-local { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 1010; }
.contact-modal-local.is-active { display: block; }
.contact-modal-local .modal-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.6); animation: fadeIn 0.3s ease; }
.contact-modal-local .modal-content-local { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 900px; background: #fff; border-radius: 15px; box-shadow: 0 10px 30px rgba(0,0,0,0.2); animation: slideDown 0.4s ease-out; padding: 40px; }
.contact-modal-local .modal-close { position: absolute; top: 15px; right: 20px; font-size: 2rem; color: #aaa; background: none; border: none; cursor: pointer; line-height: 1; }
.contact-modal-local .section-header { text-align: center; margin-bottom: 40px; }
.contact-modal-local .section-header .subtitle { color: #0056b3; }
.contact-modal-local .section-header h2 { font-size: 2rem; }
.contact-modal-local .section-header p { max-width: 100%; }
.contact-wrapper { display: flex; gap: 40px; }
.contact-info, .contact-form { flex: 1; }
.contact-info h3, .contact-form h3 { font-size: 1.3rem; margin-bottom: 20px; border-bottom: 2px solid #eee; padding-bottom: 15px; }
.info-item { margin-bottom: 15px; }
.info-item strong { display: block; font-weight: 700; margin-bottom: 5px; }
.info-item p { margin: 0; color: #666; }
.contact-form input, .contact-form textarea { width: 100%; padding: 12px; border: 1px solid #ddd; border-radius: 5px; margin-bottom: 15px; }
.contact-form .cta-button { width: 100%; padding: 15px; }
.contact-form .cta-button:disabled { background-color: #ccc; cursor: not-allowed; }
.form-message-area .message { padding: 10px; margin-bottom: 15px; border-radius: 5px; text-align: center; }
.form-message-area .success { background-color: #d4edda; color: #155724; }
.form-message-area .error { background-color: #f8d7da; color: #721c24; }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes slideDown { from { opacity: 0; transform: translate(-50%, -60%); } to { opacity: 1; transform: translate(-50%, -50%); } }
@media (max-width: 768px) {
.contact-wrapper { flex-direction: column; }
.contact-modal-local .modal-content-local { padding: 25px; max-height: 85vh; overflow-y: auto; }
}
@@ -0,0 +1,78 @@
function initContactModule(moduleId) {
const moduleElement = document.getElementById(moduleId);
if (!moduleElement || moduleElement.classList.contains('initialized')) return;
const openBtn = moduleElement.querySelector('.btn-open-contact-modal');
const modal = moduleElement.querySelector('.contact-modal-local');
const closeBtn = modal.querySelector('.modal-close');
const overlay = modal.querySelector('.modal-overlay');
const form = modal.querySelector('form');
const formMessages = modal.querySelector('.form-message-area');
const openModal = () => {
modal.classList.add('is-active');
document.body.style.overflow = 'hidden';
};
const closeModal = () => {
modal.classList.remove('is-active');
document.body.style.overflow = '';
};
openBtn.addEventListener('click', openModal);
closeBtn.addEventListener('click', closeModal);
overlay.addEventListener('click', closeModal);
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && modal.classList.contains('is-active')) {
closeModal();
}
});
// 💡 [핵심 수정] universalMailer를 사용한 폼 제출 로직
form.addEventListener('submit', function(e) {
e.preventDefault();
const submitBtn = this.querySelector('button[type="submit"]');
const originalBtnText = submitBtn.textContent;
submitBtn.disabled = true;
submitBtn.textContent = '전송 중...';
formMessages.innerHTML = ''; // 이전 메시지 초기화
const formData = new FormData(form);
const variables = {
contact_name: formData.get('contact_name'),
contact_hp: formData.get('contact_hp'),
contact_email: formData.get('contact_email'),
contact_message: formData.get('contact_message').replace(/\n/g, '<br>')
};
// universalMailer 객체가 있는지 확인
if (window.universalMailer && typeof window.universalMailer.send === 'function') {
window.universalMailer.send({
template_code: 'contact_inquiry', // 관리자에서 생성한 템플릿 코드
// to_email: variables.contact_email, //수신자임 문의 하기 같은경우 관리자가 받아야 해서 비워둔다
variables: variables,
onSuccess: function(response) {
alert(response.message || '상담 신청이 성공적으로 접수되었습니다.');
form.reset();
closeModal();
},
onError: function(errorMessage) {
formMessages.innerHTML = `<div class="message error">${errorMessage || '오류가 발생했습니다.'}</div>`;
},
onComplete: function() {
submitBtn.disabled = false;
submitBtn.textContent = originalBtnText;
}
});
} else {
alert('메일 발송 기능에 오류가 발생했습니다. 관리자에게 문의해주세요.');
submitBtn.disabled = false;
submitBtn.textContent = originalBtnText;
}
});
moduleElement.classList.add('initialized');
}
window.initContactModule = initContactModule;
@@ -0,0 +1,87 @@
<?php
if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가
// CSS와 JS 파일의 버전을 파일 수정 시간으로 자동 갱신
$module_css_path = G5_THEME_PATH.'/rb.custom/contact/module.css';
$module_js_path = G5_THEME_PATH.'/rb.custom/contact/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 = 'contact_module_'.uniqid();
?>
<!-- 모듈의 가장 바깥 요소에 고유 ID를 부여합니다. -->
<div id="<?php echo $module_id; ?>" class="contact-module">
<!-- 💡 [핵심] 이 모듈에 종속된 모달 HTML (사용자가 제공한 디자인 적용) -->
<div class="contact-modal-local">
<div class="modal-overlay"></div>
<div class="modal-content-local">
<button type="button" class="modal-close">&times;</button>
<div class="section-header">
<span class="subtitle">Contact Us</span>
<h2>공간의 가능성을 열어보세요.</h2>
<p>성진미도어의 전문가와 상담하고, 당신의 공간에 꼭 맞는 솔루션을 찾아보세요.</p>
</div>
<div class="contact-wrapper">
<div class="contact-info">
<h3>Information</h3>
<div class="info-item"><strong>본사 및 전시장</strong><p>충청남도 아산시 음봉면 월산로 128-130</p></div>
<div class="info-item"><strong>대표 연락처</strong><p>T. 041-532-0555 / H. 010-5434-4126</p></div>
<div class="info-item"><strong>청주 전시장</strong><p>T. 043-235-2352 / H. 010-2066-4126</p></div>
<div class="info-item"><strong>운영시간</strong><p>평일 09:00 - 18:00 (주말 및 공휴일 휴무)</p></div>
</div>
<form name="fcontactform_<?php echo $module_id; ?>" id="fcontactform_<?php echo $module_id; ?>" class="contact-form" method="post">
<h3>상담 신청</h3>
<div class="form-message-area"></div>
<input type="text" name="contact_name" placeholder="이름" required>
<input type="tel" name="contact_hp" placeholder="연락처" required>
<input type="email" name="contact_email" placeholder="이메일" required>
<textarea name="contact_message" placeholder="문의 내용을 입력해주세요." rows="5" required></textarea>
<button type="submit" class="cta-button">상담 신청하기</button>
</form>
</div>
</div>
</div>
<!-- 💡 [핵심] 모듈의 실제 내용 (페이지에 표시될 부분) -->
<div class="contact-section-trigger">
<div class="container">
<div class="section-header">
<span class="subtitle">Get a Quote</span>
<h2>궁금한 점이 있으신가요?</h2>
<p>전문가의 상담을 통해 당신의 공간에 가장 적합한 솔루션을 찾아보세요.</p>
</div>
<button type="button" class="btn-open-contact-modal cta-button">무료 상담 신청하기</button>
</div>
</div>
</div>
<!-- 이 모듈에 필요한 CSS와 JS를 불러옵니다. -->
<link rel="stylesheet" href="<?php echo G5_THEME_URL; ?>/rb.custom/contact/module.css?ver=<?php echo $module_css_ver; ?>">
<script>
(function() {
const currentModuleId = '<?php echo $module_id; ?>';
const scriptId = 'contact-module-script';
if (document.getElementById(scriptId)) {
if (typeof window.initContactModule === 'function') {
window.initContactModule(currentModuleId);
}
return;
}
const script = document.createElement('script');
script.id = scriptId;
script.src = '<?php echo G5_THEME_URL; ?>/rb.custom/contact/module.js?ver=<?php echo $module_js_ver; ?>';
script.async = true;
script.onload = () => {
if (typeof window.initContactModule === 'function') {
window.initContactModule(currentModuleId);
}
};
document.head.appendChild(script);
})();
</script>
@@ -0,0 +1,31 @@
<?php
if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가
// 1. 탭 모듈 함수가 정의된 파일을 포함합니다.
include_once(G5_THEME_PATH.'/rb.custom/tabs/tab_module.php');
// 2. 탭으로 표시할 내용을 배열로 정의합니다.
$main_tabs_config = [
[
'title' => '공지사항', // 탭에 표시될 이름
'type' => 'board', // 타입: 'board' (게시판)
'id' => 'notice', // 게시판 ID (bo_table)
'options' => ['rows' => 5, 'subject_len' => 40] // 옵션
],
[
'title' => '회사소개', // 탭에 표시될 이름
'type' => 'content', // 타입: 'content' (콘텐츠 페이지)
'id' => 'company', // 콘텐츠 ID (co_id)
],
[
'title' => '갤러리',
'type' => 'board',
'id' => 'gallery',
'options' => ['rows' => 5, 'subject_len' => 35]
],
];
// 3. 설정 배열을 인자로 하여 탭 모듈 함수를 호출하고 출력합니다.
echo rb_tabs($main_tabs_config);
?>
@@ -0,0 +1,301 @@
/* --- STORY 섹션 레이아웃 --- */
.story-section {
width: 100%;
padding: 80px 0;
background-color: #f8f9fa;
overflow: hidden;
}
/* 📌 [분할] 내용과 미디어가 함께 있는 경우 (좌우 2단) */
.story-layout-split {
display: flex;
align-items: stretch;
gap: 10px;
}
.story-layout-split .story-text {
flex: 1;
min-width: 0;
text-align: left; /* 기본 좌측 정렬 */
display: flex;
flex-direction: column;
justify-content: center;
}
/* 💡 [수정] 분할 레이아웃일 때 제목(h2)만 가운데 정렬 */
.story-layout-split .story-text h2 {
text-align: center;
}
.story-layout-split .story-image {
flex: 1;
min-width: 0;
display: block; /* 이미지 영역 보이기 */
border-radius: 12px;
overflow: hidden;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
/*aspect-ratio: 4 / 3;*/
}
/* 📌 [전체] 내용만 있는 경우 (텍스트가 중앙에 넓게 배치) */
.story-layout-full {
display: flex;
justify-content: center;
}
.story-layout-full .story-text {
flex: none;
width: 100%;
max-width: 800px;
margin: 0 auto;
text-align: center; /* 모든 텍스트 가운데 정렬 */
}
.story-layout-full .story-image {
display: none; /* 이미지/슬라이더 영역 숨김 */
}
/* 공통 텍스트 스타일 */
.story-text .subtitle {
font-size: 16px;
font-weight: 700;
color: var(--accent-color);
margin-bottom: 10px;
display: block;
text-align: center; /* 💡 추가: 텍스트 가운데 정렬 */
}
/* h2는 각 레이아웃 클래스에서 정렬을 제어하므로 여기서는 공통 스타일만 정의 */
.story-text h2 {
font-size: 36px;
font-weight: 900;
color: var(--primary-color);
margin-bottom: 20px;
line-height: 1.4;
}
.story-text p {
font-size: 16px;
line-height: 1.8;
color: var(--secondary-color);
}
.story-text img,
.story-text video {
max-width: 100%;
height: auto;
border-radius: 8px;
margin: 20px 0;
}
/* --- 슬라이더(Swiper) 스타일 --- */
.story-slider {
width: 100%;
height: 100%;
}
.story-slider .swiper-slide {
width: 100%;
height: 100%;
}
.story-slider img,
.story-slider video {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.story-slider .swiper-pagination-bullet {
background-color: rgba(0, 0, 0, 0.5);
opacity: 1;
}
.story-slider .swiper-pagination-bullet-active {
background-color: var(--accent-color);
}
/* 📌 [추가] DB에 게시물이 없을 때의 기본 레이아웃 (위아래 배치) */
.story-layout-static {
display: flex;
flex-direction: column; /* 💡 핵심: 아이템을 세로로 쌓습니다. */
align-items: center; /* 가운데 정렬 */
gap: 40px; /* 텍스트와 이미지 사이의 간격 */
text-align: center; /* 모든 텍스트 가운데 정렬 */
}
.story-layout-static .story-text {
max-width: 800px; /* 텍스트 최대 너비 제한 */
}
.story-layout-static .story-image {
width: 100%;
max-width: 800px; /* 이미지 최대 너비 제한 */
}
.story-layout-static .story-image img {
width: 100%;
border-radius: 12px;
}
/* 반응형 스타일 */
@media (max-width: 768px) {
.story-layout-split {
flex-direction: column;
}
.story-text h2 {
font-size: 28px;
}
}
/* --- 10. [모듈 전용] 이미지 팝업 모달 스타일 (개선) --- */
.image-modal {
display: none; /* 기본적으로 숨김 */
position: fixed;
z-index: 1050;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto; /* 내용이 클 경우 스크롤 */
background-color: rgba(0, 0, 0, 0.85);
justify-content: center;
align-items: center;
padding: 40px 20px; /* 화면 가장자리와 여백 */
opacity: 0;
transition: opacity 0.3s ease;
}
.image-modal.is-active {
display: flex;
opacity: 1;
}
.modal-content {
position: relative;
background-color: #fff;
margin: auto;
padding: 0;
border-radius: 8px;
/* 💡 [수정] 고정된 max-width를 제거하여 이미지 크기에 맞춰 유연하게 변하도록 합니다. */
width: auto; /* 너비를 자동으로 설정 */
max-width: 100%; /* 화면 너비를 넘지 않도록 설정 */
box-shadow: 0 5px 15px rgba(0,0,0,.5);
animation: modal-slide-down 0.4s ease-out;
/*overflow: hidden;*/
display: flex; /* 내부 요소 정렬을 위해 flex 사용 */
flex-direction: column; /* 이미지와 텍스트를 세로로 쌓음 */
}
@keyframes modal-slide-down {
from {
transform: translateY(-50px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
/* 💡 [핵심 수정] 이미지 자체에 최대 크기를 지정합니다. */
.modal-content .modal-image {
display: block;
/* 화면 너비의 90%를 넘지 않도록 설정 */
max-width: 90vw;
/* 화면 높이의 80%를 넘지 않도록 설정 (텍스트 공간 확보) */
max-height: 80vh;
/* 💡 [핵심] 이미지가 잘리지 않고, 비율을 유지하며 주어진 공간 안에 꽉 차게 표시됩니다. */
object-fit: contain;
/* 💡 [수정] width와 height를 auto로 설정하여 원본 비율을 따르도록 합니다. */
width: auto;
height: auto;
}
.modal-info {
padding: 20px 25px;
/* 💡 [추가] 텍스트가 많아질 경우를 대비 */
max-width: 800px; /* 텍스트 영역의 최대 너비는 제한 */
width: 100%;
}
.modal-info .modal-title {
font-size: 1.5rem;
font-weight: 700;
color: #333;
margin: 0 0 10px 0;
}
.modal-info .modal-desc {
font-size: 1rem;
color: #666;
line-height: 1.6;
margin: 0;
}
.close-btn {
color: #fff;
position: absolute;
top: 15px;
right: 25px;
font-size: 35px;
font-weight: bold;
cursor: pointer;
transition: color 0.2s;
text-shadow: 0 1px 3px rgba(0,0,0,0.5);
z-index: 10; /* 이미지 위에 표시되도록 z-index 추가 */
}
.close-btn:hover,
.close-btn:focus {
color: #ccc;
text-decoration: none;
}
/* --- 11. [개선] 가독성 향상을 위한 줄바꿈 처리 --- */
.section-header h2,
.section-header p,
.product-info-trend h3,
.product-info-trend p,
.item-info h3,
.item-info p {
/* 💡 [핵심] 한글 단어가 중간에 잘리는 현상을 방지합니다. */
word-break: keep-all;
/* 💡 [보완] keep-all을 보완: 매우 긴 영단어나 URL이 있을 경우
강제로 줄바꿈하여 레이아웃이 깨지는 것을 방지합니다. */
overflow-wrap: break-word;
}
/* --- 12. [개선] 카드 설명 텍스트 잘림 방지 및 높이 고정 --- */
.product-info-trend p,
.item-info p {
/* 1. 텍스트가 여러 줄로 표시될 수 있도록 최소/최대 높이를 확보합니다. */
/* (폰트 크기 0.95rem * 줄간격 1.6 * 2줄) */
height: 3.04rem;
/* 2. 웹킷 브라우저(크롬, 사파리 등)를 위한 멀티라인 생략(...) 처리 */
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2; /* 💡 최대 2줄까지만 표시합니다. */
overflow: hidden;
text-overflow: ellipsis;
/* 3. 줄간격을 다시 한번 명확하게 지정합니다. */
line-height: 1.6;
}
/* --- 13. [개선] 모달 스크롤바 스타일링 (보였다 사라지는 효과) --- */
/* Firefox 스크롤바 스타일 */
.image-modal {
scrollbar-width: thin;
scrollbar-color: rgba(0, 0, 0, 0.4) transparent;
}
/* Webkit 브라우저 (크롬, 사파리 등) 스크롤바 스타일 */
.image-modal::-webkit-scrollbar {
width: 10px;
}
.image-modal::-webkit-scrollbar-track {
background: transparent; /* 트랙은 항상 투명 */
}
/* 기본 상태에서는 스크롤바 막대를 투명하게 만듭니다. */
.image-modal::-webkit-scrollbar-thumb {
background-color: transparent;
border-radius: 10px;
transition: background-color 0.3s ease;
}
/* 모달 위에 마우스를 올렸을 때만 스크롤바 막대가 보이도록 합니다. */
.image-modal:hover::-webkit-scrollbar-thumb {
background-color: rgba(0, 0, 0, 0.4);
}
@@ -0,0 +1,92 @@
// 이 스크립트는 main_visual 모듈이 로드될 때 즉시 실행됩니다.
(function() {
const storySliderEl = document.querySelector('.story-slider');
// 슬라이더 요소가 없거나, 이미 초기화되었다면 중복 실행을 방지합니다.
if (!storySliderEl || storySliderEl.classList.contains('swiper-initialized')) {
return;
}
/**
* 슬라이더 내의 모든 비디오를 찾아 리셋하는 함수
* @param {HTMLElement} sliderElement - 슬라이더 컨테이너 요소
*/
const resetAllVideos = (sliderElement) => {
sliderElement.querySelectorAll('video').forEach(video => {
if (!video.paused) {
video.pause();
}
video.currentTime = 0;
});
};
/**
* 현재 활성화된 슬라이드를 처리하는 함수
* @param {object} swiper - Swiper 인스턴스
*/
const handleActiveSlide = (swiper) => {
// 💡 [안정성 강화] swiper 객체가 유효한지 확인 후 진행합니다.
if (!swiper || !swiper.el) {
console.error("handleActiveSlide: 유효하지 않은 Swiper 인스턴스입니다.");
return;
}
// 다른 모든 비디오를 먼저 리셋합니다.
resetAllVideos(swiper.el);
const activeSlide = swiper.slides[swiper.activeIndex];
const activeVideo = activeSlide?.querySelector('video');
if (activeSlide?.dataset?.type === 'video' && activeVideo) {
// 활성 슬라이드가 비디오일 경우, 자동재생을 멈춥니다.
swiper.autoplay.stop();
// 비디오를 재생합니다. (브라우저 정책에 의한 실패 대비)
activeVideo.play().catch(error => {
console.error("비디오 자동재생이 차단되었습니다:", error);
// 재생에 실패하면 다음 슬라이드로 즉시 이동합니다.
swiper.slideNext();
});
// 비디오가 끝나면 다음 슬라이드로 이동하도록 이벤트를 설정합니다.
activeVideo.onended = () => {
swiper.slideNext();
};
} else {
// 활성 슬라이드가 이미지일 경우, 자동재생을 다시 시작합니다.
swiper.autoplay.start();
}
};
// Swiper 라이브러리가 로드되었는지 확인 후 슬라이더를 초기화합니다.
if (typeof Swiper !== 'undefined') {
const swiper = new Swiper(storySliderEl, {
loop: true,
effect: 'fade',
fadeEffect: {
crossFade: true
},
pagination: {
el: '.swiper-pagination',
clickable: true,
},
autoplay: {
delay: 5000, // 💡 [개선] 비디오 재생을 고려하여 딜레이를 5초로 늘립니다.
disableOnInteraction: false,
},
on: {
// 💡 [수정] 'this'를 사용하여 Swiper 인스턴스를 안정적으로 전달합니다.
slideChange: function () {
handleActiveSlide(this);
},
init: function () {
handleActiveSlide(this);
}
}
});
// 초기화되었음을 표시하여 중복 실행을 막습니다.
storySliderEl.classList.add('swiper-initialized');
} else {
console.error('Swiper 라이브러리가 로드되지 않았습니다.');
}
})();
@@ -0,0 +1,80 @@
<?php
if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가
// --- (기존 PHP 데이터 처리 코드는 그대로 둡니다) ---
$main_visual_post = null;
$main_visual_files = [];
$layout_class = '';
$today = G5_TIME_YMD;
$sql = " SELECT * FROM {$g5['write_prefix']}main_visual
WHERE wr_is_comment = 0 AND wr_10 = '0' AND (wr_1 = 'IMMEDIATE' OR (wr_1 = 'RESERVED' AND '{$today}' BETWEEN wr_2 AND wr_3))
ORDER BY CASE wr_1 WHEN 'IMMEDIATE' THEN 1 ELSE 2 END, wr_num, wr_reply LIMIT 1 ";
$main_visual_post = sql_fetch($sql);
if ($main_visual_post) {
$layout_class = 'story-layout-full';
$temp_files = get_file('main_visual', $main_visual_post['wr_id']);
if (isset($temp_files) && $temp_files['count'] > 0) {
for ($i = 0; $i < $temp_files['count']; $i++) {
if (!isset($temp_files[$i]['file']) || !$temp_files[$i]['file']) continue;
$file_ext = strtolower(pathinfo($temp_files[$i]['file'], PATHINFO_EXTENSION));
$is_video = in_array($file_ext, ['mp4', 'mov', 'webm']);
$main_visual_files[] = ['url' => $temp_files[$i]['path'].'/'.$temp_files[$i]['file'], 'type' => $is_video ? 'video' : 'image'];
}
}
$has_media_in_content = preg_match('/<img|<video/i', $main_visual_post['wr_content']);
if ($has_media_in_content) {
$layout_class = (count($main_visual_files) > 0) ? 'story-layout-split' : 'story-layout-full';
} else {
$layout_class = (count($main_visual_files) > 0) ? 'story-layout-static' : 'story-layout-full';
}
} else {
$layout_class = 'story-layout-static';
}
// --- (PHP 코드 끝) ---
?>
<!-- 💡 [최종 수정] section에 'rb-autounwrap' 클래스를 추가하여 스마트 모듈임을 선언합니다. -->
<section id="story" class="story-section">
<div class="container <?php echo $layout_class; ?>">
<?php if ($main_visual_post) { // 표시할 게시물이 있을 경우 ?>
<div class="story-text">
<span class="subtitle">Our Vision</span>
<h2><?php echo get_text($main_visual_post['wr_subject']); ?></h2>
<?php echo conv_content($main_visual_post['wr_content'], 1); ?>
</div>
<div class="story-image">
<?php if (count($main_visual_files) > 0) { // 첨부파일이 있을 경우 슬라이더 표시 ?>
<div class="swiper-container story-slider">
<div class="swiper-wrapper">
<?php foreach ($main_visual_files as $file) { ?>
<div class="swiper-slide" data-type="<?php echo $file['type']; ?>">
<?php if ($file['type'] === 'video') { ?>
<video src="<?php echo $file['url']; ?>" muted playsinline loop autoplay></video>
<?php } else { ?>
<img src="<?php echo $file['url']; ?>" alt="메인 비주얼 이미지">
<?php } ?>
</div>
<?php } ?>
</div>
<div class="swiper-pagination"></div>
</div>
<?php } ?>
</div>
<?php } else { // 표시할 게시물이 없을 경우 (기존 정적 내용) ?>
<div class="story-text">
<span class="subtitle">Our Vision</span>
<h2>문을 열면, 삶이 달라져야 한다고 믿습니다.</h2>
<p>성진미도어는 국내 중문 시장 최초로 여닫이와 미닫이 기능을 복합한 ‘프리도어’를 개발했습니다. 시판과 특판 모두를 아우를 수 있는 기술력으로, 일상의 흐름을 바꾸는 문을 만듭니다. 중문 시장의 한줄기 빛이 되도록, 오늘도 한 걸음 더 나아갑니다.</p>
</div>
<div class="story-image">
<img src="<?php echo G5_THEME_URL ?>/img/mainimage/main1.png" alt="미래 지향적인 디자인의 문">
</div>
<?php } ?>
</div>
</section>
<!-- 💡 [핵심] 이 모듈에 필요한 CSS와 JS 파일을 모두 이곳에서 직접 불러옵니다. -->
<link rel="stylesheet" href="<?php echo G5_THEME_URL; ?>/rb.custom/main_visual/module.css?ver=<?php echo G5_CSS_VER; ?>">
<script src="<?php echo G5_THEME_URL; ?>/rb.custom/main_visual/module.js?ver=<?php echo G5_JS_VER; ?>"></script>
@@ -0,0 +1,272 @@
/*
* ==========================================================================
* 💡 products_section 모듈 전용 스타일시트
* ==========================================================================
*/
/* --- 1. 섹션 기본 스타일 --- */
.products-trend-section {
width: 100%;
padding: 80px 0;
background-color: #ffffff; /* 또는 원하는 배경색 */
}
/* --- 2. 섹션 헤더 (제목, 부제) --- */
.section-header {
text-align: center;
margin-bottom: 50px;
}
.section-header .subtitle {
font-size: 16px;
font-weight: 700;
color: #0056b3; /* 포인트 색상 */
margin-bottom: 10px;
display: block;
}
.section-header h2 {
font-size: 36px;
font-weight: 900;
color: #25282B; /* 기본 텍스트 색상 */
margin-bottom: 15px;
line-height: 1.4;
}
.section-header p {
font-size: 16px;
line-height: 1.7;
color: #666;
max-width: 600px;
margin: 0 auto;
}
/* --- 3. 제품 카드 그리드 --- */
.product-grid {
display: grid;
/* 💡 화면 크기에 따라 1~3개의 컬럼으로 자동 조정됩니다. */
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
}
/* --- 4. 개별 제품 카드 --- */
.product-card-trend {
background-color: #fff;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
transition: transform 0.3s ease, box-shadow 0.3s ease;
cursor: pointer;
}
.product-card-trend:hover {
transform: translateY(-5px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
}
/* --- 5. 카드 내부 요소 --- */
.product-image-trend {
width: 100%;
/* 💡 이미지 비율을 4:3으로 유지합니다. */
aspect-ratio: 4 / 3;
overflow: hidden;
}
.product-image-trend.is-patent {
padding: 20px;
background-color: #f0f0f0;
}
.product-image-trend img {
width: 100%;
height: 100%;
object-fit: cover; /* 이미지가 잘리지 않고 꽉 차도록 설정 */
transition: transform 0.4s ease;
}
.product-card-trend:hover .product-image-trend img {
transform: scale(1.05);
}
.product-info-trend {
padding: 25px;
}
.product-info-trend h3 {
font-size: 1.2rem;
font-weight: 700;
margin-bottom: 10px;
color: #333;
}
.product-info-trend p {
font-size: 0.95rem;
color: #666;
line-height: 1.6;
}
/* --- 9. 스크롤 애니메이션 (style_prestige_1.css에서 가져옴) --- */
.reveal-up, .reveal-fade {
opacity: 0;
transition: opacity 0.8s ease-out, transform 0.8s ease-out;
}
.reveal-up { transform: translateY(40px); }
.reveal-fade { transform: scale(0.95); }
.reveal-up.is-revealed, .reveal-fade.is-revealed {
opacity: 1;
transform: none;
}
/* --- 10. [모듈 전용] 이미지 팝업 모달 스타일 (개선) --- */
.image-modal {
display: none; /* 기본적으로 숨김 */
position: fixed;
z-index: 1050;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto; /* 내용이 클 경우 스크롤 */
background-color: rgba(0, 0, 0, 0.85);
justify-content: center;
align-items: center;
padding: 40px 20px; /* 화면 가장자리와 여백 */
opacity: 0;
transition: opacity 0.3s ease;
}
.image-modal.is-active {
display: flex;
opacity: 1;
}
.modal-content {
position: relative;
background-color: #fff;
margin: auto;
padding: 0;
border-radius: 8px;
width: auto; /* 너비를 자동으로 설정 */
max-width: 100%; /* 화면 너비를 넘지 않도록 설정 */
box-shadow: 0 5px 15px rgba(0,0,0,.5);
animation: modal-slide-down 0.4s ease-out;
display: flex; /* 내부 요소 정렬을 위해 flex 사용 */
flex-direction: column; /* 이미지와 텍스트를 세로로 쌓음 */
}
@keyframes modal-slide-down {
from {
transform: translateY(-50px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
.modal-content .modal-image {
display: block;
max-width: 90vw;
max-height: 80vh;
object-fit: contain;
width: auto;
height: auto;
}
.modal-info {
padding: 20px 25px;
max-width: 800px; /* 텍스트 영역의 최대 너비는 제한 */
width: 100%;
}
.modal-info .modal-title {
font-size: 1.5rem;
font-weight: 700;
color: #333;
margin: 0 0 10px 0;
}
.modal-info .modal-desc {
font-size: 1rem;
color: #666;
line-height: 1.6;
margin: 0;
}
.close-btn {
color: #fff;
position: absolute;
top: 15px;
right: 25px;
font-size: 35px;
font-weight: bold;
cursor: pointer;
transition: color 0.2s;
text-shadow: 0 1px 3px rgba(0,0,0,0.5);
z-index: 10;
}
.close-btn:hover,
.close-btn:focus {
color: #ccc;
text-decoration: none;
}
/* --- 11. [개선] 가독성 향상을 위한 줄바꿈 처리 --- */
.section-header h2,
.section-header p,
.product-info-trend h3,
.product-info-trend p,
.item-info h3,
.item-info p {
word-break: keep-all;
overflow-wrap: break-word;
}
/* --- 12. [개선] 카드 설명 텍스트 잘림 방지 및 높이 고정 --- */
.product-info-trend p,
.item-info p {
height: 3.04rem;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
line-height: 1.6;
}
/* --- 13. [최종 수정] 모듈 스크롤바 완전 숨김 처리 --- */
/* 1. 기본 상태: 스크롤바를 완전히 투명하게 만듭니다. */
.image-modal {
/* Firefox: 스크롤바 색상을 양쪽 모두 투명하게 설정 */
scrollbar-color: transparent transparent;
scrollbar-width: thin;
transition: scrollbar-color 0.3s ease;
}
.image-modal::-webkit-scrollbar {
width: 10px;
}
.image-modal::-webkit-scrollbar-track {
background: transparent;
}
.image-modal::-webkit-scrollbar-thumb {
/* Webkit: 스크롤바 막대를 투명하게 */
background-color: transparent;
border-radius: 10px;
border: 3px solid transparent;
background-clip: padding-box;
transition: background-color 0.3s ease;
}
/* 2. :hover 상태: 마우스를 올리면 스크롤바가 나타납니다. */
.image-modal:hover {
/* Firefox: 스크롤바 색상을 보이게 변경 */
scrollbar-color: rgba(0, 0, 0, 0.4) transparent;
}
.image-modal:hover::-webkit-scrollbar-thumb {
/* Webkit: 스크롤바 막대 색상을 보이게 변경 */
background-color: rgba(0, 0, 0, 0.4);
}
@@ -0,0 +1,101 @@
// 💡 [핵심 수정] 모든 로직을 초기화 함수 안으로 옮기고, 모듈의 고유 ID를 인자로 받도록 변경합니다.
function initProductsSectionModule(moduleId) {
// 💡 [핵심 수정] document가 아닌, 전달받은 moduleId를 기준으로 요소를 찾습니다.
const moduleElement = document.getElementById(moduleId);
if (!moduleElement) return;
// 이미 초기화된 모듈은 다시 실행하지 않습니다.
if (moduleElement.classList.contains('initialized')) {
return;
}
const section = moduleElement.querySelector('.products-trend-section');
const productGrid = moduleElement.querySelector('.product-grid');
const modal = moduleElement.querySelector('.image-modal');
if (!section || !productGrid || !modal) {
console.error('Module elements not found in:', moduleId);
return;
}
/**
* 그리드에 제품 카드를 렌더링하는 함수
*/
function renderGrid() {
const productsJson = section.dataset.products;
let productsData;
try {
productsData = JSON.parse(productsJson);
} catch (e) {
productGrid.innerHTML = '<p style="text-align:center; padding: 40px 0; color: #d9534f;">제품 데이터를 불러오는 데 실패했습니다.</p>';
return;
}
if (!productsData || productsData.length === 0) {
productGrid.innerHTML = '<p style="text-align:center; padding: 40px 0; color: #888;">표시할 제품이 없습니다.</p>';
return;
}
const productCardsHTML = productsData.map(item => `
<div class="product-card-trend js-modal-trigger" data-title="${item.title}" data-desc="${item.description}" data-img="${item.image}">
<div class="product-image-trend">
<img src="${item.image}" alt="${item.title}">
</div>
<div class="product-info-trend">
<h3>${item.title}</h3>
<p>${item.description}</p>
</div>
</div>
`).join('');
productGrid.innerHTML = productCardsHTML;
}
/**
* 모달 관련 이벤트를 설정하는 함수
*/
function setupModalEvents() {
const modalImage = modal.querySelector('.modal-image');
const modalTitle = modal.querySelector('.modal-title');
const modalDesc = modal.querySelector('.modal-desc');
const closeBtn = modal.querySelector('.close-btn');
// 모달 열기 (이벤트 위임)
productGrid.addEventListener('click', function(event) {
const card = event.target.closest('.js-modal-trigger');
if (card) {
modalImage.src = card.dataset.img;
modalTitle.textContent = card.dataset.title;
modalDesc.textContent = card.dataset.desc;
modal.classList.add('is-active');
}
});
// 모달 닫기 함수
function closeModal() {
modal.classList.remove('is-active');
}
// 이벤트 리스너 등록
closeBtn.addEventListener('click', closeModal);
modal.addEventListener('click', function(event) {
if (event.target === modal) closeModal();
});
document.addEventListener('keydown', function(event) {
if (event.key === 'Escape' && modal.classList.contains('is-active')) {
closeModal();
}
});
}
// 함수 실행
renderGrid();
setupModalEvents();
// 초기화 완료 클래스 추가
moduleElement.classList.add('initialized');
}
// 💡 [핵심 수정] 다른 곳에서 이 함수를 다시 호출할 수 있도록 전역에 노출시킵니다.
window.initProductsSectionModule = initProductsSectionModule;
@@ -0,0 +1,97 @@
<?php
if (!defined('_GNUBOARD_')) exit;
// --- 데이터 처리 ---
$products_data = array();
$sql = " SELECT wr_id, wr_subject, wr_content FROM {$g5['write_prefix']}products WHERE wr_is_comment = 0 ORDER BY wr_num, wr_reply LIMIT 3 ";
$result = sql_query($sql);
for ($i=0; $row=sql_fetch_array($result); $i++) {
$files = get_file('products', $row['wr_id']);
$image_url = (isset($files[0]['path']) && isset($files[0]['file'])) ? $files[0]['path'].'/'.$files[0]['file'] : G5_THEME_URL.'/img/no_image.png';
$products_data[] = array(
'id' => $row['wr_id'],
'title' => get_text($row['wr_subject']),
'description' => get_text(cut_str(strip_tags($row['wr_content']), 100)),
'image' => $image_url
);
}
$products_json = json_encode($products_data, JSON_UNESCAPED_UNICODE);
// CSS와 JS 파일의 버전을 파일 수정 시간으로 자동 갱신
$module_css_path = G5_THEME_PATH.'/rb.custom/products_section/module.css';
$module_js_path = G5_THEME_PATH.'/rb.custom/products_section/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 = 'products_section_'.uniqid();
?>
<!-- 💡 [핵심 수정] 모듈의 가장 바깥 요소에 고유 ID를 부여합니다. -->
<div id="<?php echo $module_id; ?>" class="products-section-module">
<section class="products-trend-section" data-products='<?php echo htmlspecialchars($products_json, ENT_QUOTES, 'UTF-8'); ?>'>
<div class="container">
<div class="section-header">
<span class="subtitle">Premium Door</span>
<h2>혁신과 기술로 빚어낸 프리미엄 중문</h2>
<p>성진미도어는 단순한 중문을 넘어, 공간에 새로운 가치를 더하는 혁신적인 문화를 만들어갑니다.</p>
</div>
<div class="product-grid">
<!-- JS will generate product cards here -->
</div>
</div>
</section>
<!-- 💡 [핵심 수정] 모듈 내부에 모달 HTML을 포함시킵니다. -->
<div class="image-modal">
<div class="modal-content">
<span class="close-btn">&times;</span>
<img class="modal-image" src="" alt="제품 이미지">
<div class="modal-info">
<h3 class="modal-title"></h3>
<p class="modal-desc"></p>
</div>
</div>
</div>
</div>
<!-- 이 모듈에 필요한 CSS 파일을 불러옵니다. -->
<link rel="stylesheet" href="<?php echo G5_THEME_URL; ?>/rb.custom/products_section/module.css?ver=<?php echo $module_css_ver; ?>">
<?php // AJAX로 로드될 때도 스크립트가 확실하게 실행되도록 동적 로딩 방식으로 변경합니다. ?>
<script>
(function() {
// 💡 [핵심 수정] 이 모듈의 고유 ID를 자바스크립트로 전달합니다.
const currentModuleId = '<?php echo $module_id; ?>';
const scriptId = 'products-section-module-script';
// 스크립트가 이미 로드되었는지 확인 (중복 실행 방지)
if (document.getElementById(scriptId)) {
// 이미 로드되었다면, 초기화 함수만 다시 호출
if (typeof window.initProductsSectionModule === 'function') {
window.initProductsSectionModule(currentModuleId);
}
return;
}
const script = document.createElement('script');
script.id = scriptId;
script.src = '<?php echo G5_THEME_URL; ?>/rb.custom/products_section/module.js?ver=<?php echo $module_js_ver; ?>';
script.async = true;
// 💡 [핵심 수정] 스크립트 로드가 완료되면, 고유 ID를 인자로 전달하여 초기화 함수를 호출합니다.
script.onload = () => {
if (typeof window.initProductsSectionModule === 'function') {
window.initProductsSectionModule(currentModuleId);
}
};
script.onerror = () => {
console.error('Failed to load products_section/module.js');
};
document.head.appendChild(script);
})();
</script>
@@ -0,0 +1,358 @@
@charset "utf-8";
/* ==========================================================================
서브 페이지 공통 레이아웃 (subpage common layout)
========================================================================== */
/* --- 상단 비주얼 (Top Visual) --- */
.sub-visual {
position: relative;
height: 250px;
background-size: cover;
background-position: 50% 50%;
background-repeat: no-repeat;
color: #fff;
}
/* 각 페이지별 배경 이미지 설정 (경로는 테마에 맞게 수정해주세요) */
.sub-visual.bg1 { background-image: url('../../../img/sub/sub/visual_bg_01.jpg'); } /* Hi창호는? */
.sub-visual.bg2 { background-image: url('../img/sub/visual_bg_02.jpg'); } /* Hi 고객스토리 */
.sub-visual .inner {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
text-align: center;
width: 100%;
}
.sub-visual .inner h2 {
font-size: 40px;
font-weight: 700;
}
/* --- 로컬 네비게이션 (LNB) --- */
.lnb-wrap {
position: relative;
border-bottom: 1px solid #ddd;
background: #fff;
}
.lnb {
max-width: 1200px;
margin: 0 auto;
}
.lnb .swiper-wrapper {
display: flex;
justify-content: center; /* PC에서는 중앙 정렬 */
}
.lnb .swiper-slide {
flex-shrink: 0; /* 모바일 스와이프를 위해 축소 방지 */
width: auto; /* 내용에 맞게 너비 자동 조절 */
}
.lnb .swiper-slide a {
display: block;
padding: 18px 30px;
font-size: 17px;
color: #333;
font-weight: 500;
text-align: center;
position: relative;
transition: color 0.3s;
text-decoration: none;
white-space: nowrap;
}
.lnb .swiper-slide.active a,
.lnb .swiper-slide a:hover {
color: #1c6a2f; /* 활성/호버 시 색상 */
font-weight: 700;
}
.lnb .swiper-slide.active a::after {
content: '';
position: absolute;
bottom: -1px;
left: 0;
width: 100%;
height: 3px;
background: #1c6a2f; /* 활성 메뉴 하단 바 */
}
/* --- 메인 콘텐츠 영역 (Main Contents Area) --- */
#container {
padding: 80px 20px; /* 상하, 좌우 여백 */
}
#contArea {
max-width: 1200px;
margin: 0 auto;
}
.sub-title {
text-align: center;
margin-bottom: 60px;
}
.sub-title h2 {
font-size: 36px;
font-weight: 700;
color: #222;
}
/* ==========================================================================
콘텐츠 스타일 (Contents Style)
========================================================================== */
.greetings .heading {
}
/* --- 소개 페이지 (greetings) --- */
.greetings .heading {
position: relative;
height: 350px;
padding: 50px;
margin-bottom: 60px;
display: flex;
align-items: center;
color: #fff;
background-image: linear-gradient(to right, #2e67a7, #55a13f);
}
.greetings .heading .img img {
position: absolute;
bottom: 0;
left: 108px;
}
.greetings .heading .txt {
position: relative;
margin-left: 440px;
}
.greetings .heading .txt .t2 {
font-size: 24px;
font-weight: 700;
line-height: 1.5;
margin: 0;
}
.greetings .content {
font-size: 16px;
line-height: 1.8;
color: #555;
}
.greetings .content p {
margin-bottom: 1.5em;
}
.greetings .content .lg-txt {
font-size: 1.2em;
font-weight: 600;
color: #333;
margin-top: 40px;
}
.greetings .content .text-green {
color: #1c6a2f;
}
/* --- 페이지 이동 링크 (doc-cnt) --- */
.doc-cnt {
margin-top: 80px;
border-top: 1px solid #eee;
padding-top: 40px;
}
.link-group {
display: flex;
justify-content: space-between;
}
.link-group .col a {
display: inline-flex;
align-items: center;
text-decoration: none;
color: #333;
font-size: 16px;
transition: color 0.3s;
}
.link-group .col a:hover {
color: #1c6a2f;
}
.link-group .col a i {
font-size: 20px;
margin: 0 10px;
}
/* --- 아이콘 리스트 (sub01_3.php) --- */
.icon-list {
list-style: none;
padding: 0;
margin: 0;
}
.icon-list li {
display: flex;
align-items: flex-start;
margin-bottom: 1em;
}
.icon-list i {
margin-right: 10px;
font-size: 1.2em;
line-height: 1.5;
color: #1c6a2f; /* 아이콘 색상 */
}
.icon-list .text {
flex: 1;
}
/* --- 견적 폼 (sub02.php) --- */
.form-group {
margin-bottom: 20px;
padding-bottom: 20px;
border-bottom: 1px solid #eee;
}
.form-group:last-child {
border-bottom: 0;
}
.form-group label {
font-weight: bold;
display: block;
margin-bottom: 8px;
font-size: 18px;
}
.form-group input[type="text"],
.form-group textarea {
width: 100%;
padding: 10px;
font-size: 1em;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
.form-group .options {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 8px;
align-items: center;
}
.form-group .options label {
display: flex;
align-items: center;
background: #f9f9f9;
border: 1px solid #ddd;
border-radius: 20px;
padding: 8px 15px;
cursor: pointer;
font-size: 15px;
font-weight: normal;
margin-bottom: 0;
}
.form-group .options input[type="radio"],
.form-group .options input[type="checkbox"] {
margin-right: 8px;
}
.form-group .sub-text {
font-size: 0.9em;
color: #666;
margin-top: 5px;
}
.well3 {
border: 1px solid #ddd;
padding: 20px;
border-radius: 8px;
margin-top: 20px;
background-color: #f5f5f5;
}
/* --- 버튼 스타일 --- */
.btn-group {
text-align: center;
margin-top: 40px;
}
.btn-group button {
padding: 12px 30px;
font-size: 16px;
margin: 0 10px;
border: none;
border-radius: 6px;
cursor: pointer;
transition: opacity 0.3s;
}
.btn-group button[type="submit"] {
background-color: #1c6a2f;
color: white;
}
.btn-group button[type="button"] {
background-color: #666;
color: white;
}
.btn-group button:hover {
opacity: 0.9;
}
/* ==========================================================================
반응형 스타일 (Responsive Style)
========================================================================== */
@media screen and (max-width: 992px) {
.sub-visual .inner h2 {
font-size: 32px;
}
.sub-title h2 {
font-size: 28px;
}
#container {
padding: 60px 15px;
}
}
@media screen and (max-width: 768px) {
.sub-visual {
height: 200px;
}
.sub-visual .inner h2 {
font-size: 28px;
}
.lnb .swiper-wrapper {
justify-content: flex-start; /* 모바일에서는 왼쪽부터 정렬 */
}
.lnb .swiper-slide a {
padding: 15px 20px;
font-size: 15px;
}
.greetings .heading .txt .t2 {
font-size: 18px;
}
.link-group {
flex-direction: column;
gap: 20px;
}
.link-group .col {
text-align: center;
}
.form-group .options {
flex-direction: column;
align-items: flex-start;
}
}
@@ -0,0 +1,77 @@
<?php
//if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가
// 레이아웃 폴더내 style.css 파일
include_once '../../../../common.php';
// 2. 💡 [핵심] 우리가 만든 서브페이지 전용 CSS 파일을 동적으로 불러옵니다.
// 이 코드는 반드시 head.php를 포함하기 전에 위치해야 합니다.
//add_stylesheet('<link rel="stylesheet" href="'.G5_THEME_URL.'/rb.layout/'.$rb_core['layout'].'/style.css">', 0);
add_stylesheet('<link rel="stylesheet" href="'.G5_THEME_URL.'/rb.custom/submenu/css/subpage.css?ver='.G5_SERVER_TIME.'">', 0);
include_once(G5_THEME_PATH.'/head.php');
?>
<div id="contArea">
<div class="sub-title">
<h2>Hi창호는?</h2>
</div>
<div class="real-cont">
<div class="greetings">
<!-- 💡 [삭제] <style> 태그는 subpage.css 파일로 통합되었으므로 삭제합니다. -->
<!-- *** 모바일 *** -->
<?php if (is_mobile()) { ?>
<div class="row">
<div class="col-lg-6 col-sm-6">
<!-- 💡 [수정] 이미지 경로를 테마 URL 기준으로 변경합니다. -->
<center><img src="<?php echo G5_THEME_URL; ?>/img/sub/sub/ksj.jpg" alt="Hi창호 소개 이미지"></center>
</div>
<div class="col-lg-6 col-sm-6">
<p class="t2">
<center>Hi 창호는 창호 교체를 쉽고 빠르게 도와주는 플랫폼입니다.<br>
한 번에 여러 브랜드 견적을 비교하고 최적의 선택이 가능합니다.<br>
제작부터 시공까지 믿을 수 있는 원스톱 서비스를 제공합니다.
</center>
</p>
</div>
</div>
<?php } ?>
<!-- *** PC *** -->
<?php if (!is_mobile()) { ?>
<div class="heading">
<div class="img"><img src="<?php echo G5_THEME_URL; ?>/img/sub/sub/ksj.jpg" alt="Hi창호 소개 이미지"></div>
<div class="txt">
<p class="t2">Hi 창호는 창호 교체를 쉽고 빠르게 도와주는 플랫폼입니다.<br>
한 번에 여러 브랜드 견적을 비교하고 최적의 선택이 가능합니다.<br>
제작부터 시공까지 믿을 수 있는 원스톱 서비스를 제공합니다.</p>
</div>
</div>
<?php } ?>
<div class="content">
<p>안녕하세요, Hi 창호입니다.<br>
고객의 삶의 질을 높이는 창호 교체, 이제 Hi 창호와 함께 새롭게 경험해보세요.</p>
<p>Hi 창호는 아파트, 연립주택, 빌라, 전원주택 등 다양한 주거 형태에 맞춘 창호 교체를 전문적으로 지원하는 토탈 플랫폼입니다.<br>
기존의 복잡하고 번거로운 견적 요청 과정을 획기적으로 개선하여, 단 한 번의 정보 입력만으로 여러 브랜드의 견적을 손쉽게 비교하고, 고객에게 가장 합리적인 선택지를
제안합니다.</p>
<p>저희는 창호 제작 공장에서 제품을 직접 배송하고, 숙련된 전문 시공팀이 정확하고 깔끔하게 설치까지 책임지는 <strong>원스톱 서비스</strong>를 제공합니다.<br>
단순한 교체를 넘어, 고객의 주거 환경과 예산, 취향을 반영한 <strong>맞춤형 솔루션</strong>을 통해 차별화된 품질과 만족을 약속드립니다.</p>
<p>Hi 창호는 투명한 가격, 고품질 제품, 그리고 믿을 수 있는 시공으로 고객 한 분 한 분의 소중한 공간을 더욱 가치 있게
만듭니다.<br>
편리함과 신뢰, 그리고 완성도 높은 결과까지. 창호 교체의 새로운 기준, Hi 창호가 만들어갑니다.</p>
<p>감사합니다.</p>
</div>
</div>
</div>
</div>
<?php
include_once(G5_THEME_PATH . '/tail.php');
?>
@@ -0,0 +1,134 @@
<?php
include_once './_common.php';
include_once G5_LIB_PATH.'/thumbnail.lib.php';
include_once G5_PATH.'/head.php';
?>
<!-- for mobile -->
<div id="menu" class="mobile-navigation">
<div class="home"><a href="/"><img src="/theme/rd.lwd/img/sub/common/logo.png" alt="하이창호"></a></div>
<nav class="nav-menu"></nav>
<a href="javascript:void(0);" class="close">닫기</a>
</div>
<div class="mobile-overlay"></div>
<div class="sub-visual bg1">
<div class="inner">
<h2>Hi창호</h2>
</div>
</div>
<div class="lnb-wrap">
<div class="lnb">
<div class="swiper-container">
<ul class="swiper-wrapper">
<li class="swiper-slide"><a href="/sub01.php">Hi창호는?</a></li>
<li class="swiper-slide active "><a href="/sub01_2.php">Hi창호 차별화 서비스</a></li>
<li class="swiper-slide "><a href="/sub01_3.php">창호전문상담가</a></li>
</ul>
</div>
</div>
</div>
<div id="container">
<div id="contArea">
<div class="sub-title">
<h2>Hi창호 차별화 서비스</h2>
</div>
<div class="real-cont">
<!--// content -->
<div class="greetings">
<style>
/*** 이실장님 제공소스 ***/
@media screen and (max-width:992px){
.row {
display: flex;
flex-wrap: wrap;
margin: 0 auto;
width: 100%;
flex-direction: column;}
}
</style>
<!-- *** 모바일 ------------------------------------------------------------------------>
<?php if(is_mobile()){ ?>
<div class="row">
<div class="col-lg-6 col-sm-6"><center><img src="/theme/rd.lwd/img/sub/sub/ksjj.jpg" alt=""></center></div>
<div class="col-lg-6 col-sm-6"><p class="t2"><center>수 십개 창호 브랜드 견적, 방문 견적서비스<br>
직영 시공체재</center></p></div>
</div>
<?php } ?>
<!-- *** PC ---------------------------------------------------------------------------->
<?php if(!is_mobile()){ ?>
<div class="heading">
<div class="img"><img src="/theme/rd.lwd/img/sub/sub/ksjj.jpg" alt=""></div>
<div class="txt">
<!-- <p class="t1">The best choice, the best reward</p> -->
<p class="t2">수 십개 창호 브랜드 견적, 방문 견적서비스<br>
직영 시공체재</p>
</div>
</div>
<?php } ?>
<!-- //pc ------------------------------------------------------------------------------>
<div class="content">
<p class="lg-txt"><strong class="text-green">Hi 창호 제공서비스 및 차별화1</strong><br>
<strong>수 십개 창호 브랜드 견적, 방문 견적서비스</strong></p>
<p>홈페이지 간단 입력 후 수십개 창호 브랜드 견적<br>
국내 존재하는 수십개 브랜드의 견적을 받아보실 수 있습니다.<br>
여기에 같은 브랜드에 다른 견적까지 받아 볼 수 있습니다.</p>
<p>고객이 직접 창호 사양을 입력 후 견적을 받습니다.</p>
<p>창호 견적 방문 서비스<br>
'하이' 창호 ‘창호 전문 상담가’가 집을 방문해 현장 답사 후 정확한 견적을 받을 수 있도록 도와드립니다.<br>
단, 방문 상담비는 3만원입니다.</p>
<p>입력 견적의 경우 실제 창호리모델링 진행 시 금액이 달라질 수 있지만 창호 전문 상담가가 방문해 작성된 견적은 그대로 진행합니다.<br>
창호 견적에는 ‘현장 상황’과 ‘실측 사이즈’에 따라 견적이 많이 달라집니다.</p>
<p>3만원으로 시간 절약 등 스트레스를 최소화 하세요!!!<br>
3만원 견적비는 실제 진행할 경우 2만원을 돌려드립니다.</p>
<p class="lg-txt"><strong class="text-green">Hi 창호 제공서비스 및 차별화2</strong><br>
<strong>직영시공체재</strong></p>
<p>하이’ 창호는 직영 시공체제를 운영하고 있습니다.<br>
또한 창호 제작 노하우를 갖춘 전국의 각 브랜드별 우수 창호 제작업체들을 직접 선정해 창호 제품을 공급하고 있습니다.</p>
<p>'Hi' 창호는 30년 이상 창호를 연구한 전문인력이 '하이' 창호를 믿고 가성비 좋은 창호 리모델링을 구현해 드립니다.</p>
<p>'창호' 어려우셨죠! 안심 하시고 저희만 믿고 따라와 주시면 됩니다.</p>
<p>창호 리모델링에서 창호 제품과 시공이 모두 중요합니다. 즉 50%인 제작과 50%인 시공으로 이뤄져 있습니다.<br>
어느 한 쪽도 무시할 순 없지만 제작은 기계의 힘을 빌리지만 시공은 오로지 사람에 의존하기 때문에 시공에 무게를 두는 것이 맞습니다.<br>
이에 저희 'Hi' 창호는 직영 시공체재를 유지하고 있어 안심하고 믿으셔도 됩니다.</p>
<p>3만원으로 시간 절약 등 스트레스를 최소화 하세요!!!<br>
3만원 견적비는 실제 진행할 경우 2만원을 돌려드립니다.</p>
</div>
</div>
<!-- content //-->
<p>&nbsp;</p>
<p>&nbsp;</p>
<div class="doc-cnt">
<div class="link-group">
<div class="col">
<a href="/sub01_3.php"><i class="xi-arrow-left"></i> <strong>창호 전문 상담가 바로가기</strong></a>
</div>
<div class="col">
<a href="/sub02.php"><strong>창호견적 바로가기</strong> <i class="xi-arrow-right"></i></a>
</div>
</div>
</div>
</div>
</div>
</div>
<?php
include_once(G5_PATH.'/tail.php');
?>
@@ -0,0 +1,238 @@
<?php
include_once './_common.php';
include_once G5_LIB_PATH.'/thumbnail.lib.php';
include_once G5_PATH.'/head.php';
?>
<!-- for mobile -->
<div id="menu" class="mobile-navigation">
<div class="home"><a href="/"><img src="/theme/rd.lwd/img/sub/common/logo.png" alt="하이창호"></a></div>
<nav class="nav-menu"></nav>
<a href="javascript:void(0);" class="close">닫기</a>
</div>
<div class="mobile-overlay"></div>
<div class="sub-visual bg1">
<div class="inner">
<h2>Hi창호</h2>
</div>
</div>
<div class="lnb-wrap">
<div class="lnb">
<div class="swiper-container">
<ul class="swiper-wrapper">
<li class="swiper-slide"><a href="/sub01.php">Hi창호는?</a></li>
<li class="swiper-slide"><a href="/sub01_2.php">Hi창호 차별화 서비스</a></li>
<li class="swiper-slide active "><a href="/sub01_3.php">창호전문상담가</a></li>
</ul>
</div>
</div>
</div>
<div id="container">
<div id="contArea">
<div class="sub-title">
<h2>창호전문상담가</h2>
</div>
<div class="real-cont">
<!--// content -->
<div class="greetings">
<style>
/*** 이실장님 제공소스 ***/
@media screen and (max-width:992px){
.row {
display: flex;
flex-wrap: wrap;
margin: 0 auto;
width: 100%;
flex-direction: column;}
}
</style>
<!-- *** 모바일 ------------------------------------------------------------------------>
<?php if(is_mobile()){ ?>
<div class="row">
<div class="col-lg-6 col-sm-6"><center><img src="/theme/rd.lwd/img/sub/sub/kssjj.jpg" alt=""></center></div>
<div class="col-lg-6 col-sm-6"><p class="t2"><center>'창호 전문 상담가'의 업무는<br>
창문에 대한 실측 &middot; 창문 종류 &middot; 유리 &middot; 시공 등 복합적으로 알려드립니다!</center></p></div>
</div>
<?php } ?>
<!-- *** PC ---------------------------------------------------------------------------->
<?php if(!is_mobile()){ ?>
<div class="heading">
<div class="img"><img src="/theme/rd.lwd/img/sub/sub/kssjj.jpg" alt=""></div>
<div class="txt">
<!-- <p class="t1">The best choice, the best reward</p> -->
<p class="t2">'창호 전문 상담가'의 업무는<br>
창문에 대한 실측 &middot; 창문 종류 &middot; 유리 &middot; 시공 등<br>복합적으로 알려드립니다!</p>
</div>
</div>
<?php } ?>
<!-- //pc ------------------------------------------------------------------------------>
<style>
.icon-list {
list-style: none;
padding: 0;
margin: 0;
}
.icon-list li {
display: flex;
align-items: flex-start;
margin-bottom: 1em;
}
.icon-list i {
margin-right: 10px;
font-size: 1.2em;
line-height: 1.5;
}
.icon-list .text {
flex: 1;
}
</style>
<div class="content">
<!-- 01 -->
<ul class="icon-list">
<li>
<i class="xi-view-list"></i>
<div class="text">
<font color="#000000">상담자가 어떤 일을 하는지, 그리고 상담받을 때 어떤 점을 확인해야 하는지 쉽게 설명해 드릴게요! <br>
‘창호 전문 상담가’는 우선 견적과 실제 시공 간의 편차를 줄여줍니다.<br>
현장을 방문해 실측해서 정확한 금액을 산출하기 위함입니다.</font>
</div>
</li>
<li>
<i class="xi-view-list"></i>
<div class="text">
<font color="#000000">창문에 대한 궁금한 사항, 창문 설치 과정, 시공 등의 정보를 친절히 알려드리고, 창문 설치가 안전하고 정확하게 이루어지도록 도와드려요. <br>
실측(정확한 치수 측정)부터 시공 일정 안내, 창문 교체 후 주의사항까지 모두 책임지고 안내해 드려요.</font>
</div>
</li>
</ul>
<!-- //01 -->
<p>&nbsp;</p>
<p><font color="#3366cc"><strong>1. 상담 전 사전 예약 및 방문 준비</strong></font>
<div style="height: 1px; background-color: #eee;"></div>
</p>
<p><i class="xi-check-square"></i> <strong>주부님</strong> : 창호 상담을 받고 싶은데, 어떻게 예약해야 하나요?<br>
<i class="xi-check-square"></i> <strong><font color="#ff6600">Hi창호</font></strong><br>
먼저 ‘Hi창호’ 홈페이지 방문 후 ‘방문 상담 예약’을 하세요.<br>그럼 ‘창호 전문 상담가’가 지정되어 고객님께 연락을 합니다.<br>
방문할 때는 단정한 복장으로 찾아 뵙고, 정확한 상담을 진행할 준비를 합니다.</p>
<p><i class="xi-view-list"></i> <font color="#000000">상담 전 미리 확인하는 것</font><br>
- 가족분들의 일정도 고려하여 편한 시간을 선택하세요.
</p>
<p><i class="xi-key"></i> <strong>주부님 TIP!</strong><br>
- 상담을 받기 전에 "현재 창문의 문제점"을 미리 정리해두면 상담이 훨씬 수월해요!</p>
<p>&nbsp;</p>
<p><font color="#3366cc"><strong>2. 창호 실측 – 정확한 치수를 확인하는 과정!</strong></font>
<div style="height: 1px; background-color: #eee;"></div>
</p>
<p><i class="xi-check-square"></i> <strong>주부님</strong> : 창호 실측이 왜 중요한가요?<br>
<i class="xi-check-square"></i> <strong><font color="#ff6600">Hi창호</font></strong><br>
실측이 잘못되면 창문이 틀에 맞지 않아 설치가 어려울 수 있어요. 그럼 창호를 다시 제작해야 합니다.<br>비용이 2배 발생합니다. 그래서 창문의 위치, 벽 두께, 유리 종류, 방충망 여부까지 꼼꼼하게 체크해야 해요.</p>
<p><i class="xi-view-list"></i> <font color="#000000">실측할 때 확인하는 것</font><br>
- 교체할 창문의 위치 확인<br>
- 창문이 들어갈 공간(개구부)의 가로•세로 길이 측정<br>
- 벽 두께, 가스 배관•연통 위치 확인<br>
- 창문 개폐 방향(오른쪽 or 왼쪽) 체크<br>
- 유리 사양(단열 유리, 방음 유리 등) 및 방충망 유무 확인<br>
</p>
<p><i class="xi-key"></i> <strong>주부님 TIP!</strong><br>
- 상담할 때 ‘내가 원하는 창’ 또는 ‘우리집에 필요한 창문’을 검색 등을 통해해 미리 생각해보시면 더 정확한 상담을 받을 수 있어요!</p>
<p>&nbsp;</p>
<p><font color="#3366cc"><strong>3. 현장 점검 – 시공할 때 문제가 없는지 미리 체크!</strong></font>
<div style="height: 1px; background-color: #eee;"></div>
</p>
<p><i class="xi-check-square"></i> <strong>주부님</strong> : 시공할 때 예상하지 못한 문제가 생기진 않을까요?<br>
<i class="xi-check-square"></i> <strong><font color="#ff6600">Hi창호</font></strong><br>
그런 일이 없도록 미리 현장을 점검해요.<br>
특히 <strong>가구나 세탁기 같은 가전제품과 창문의 간섭 여부</strong>를 체크하는 게 중요해요!</p>
<p><i class="xi-view-list"></i> <font color="#000000">미리 체크하는 것</font><br>
- 사다리차가 필요한지(고층일 경우)<br>
- 거주 중인 집인지, 공사 중인 집인지<br>
- 세탁기, 장롱 등 가구 간섭 여부<br>
- 세탁기가 타워형이라면 AS기사 방문이 필요한지 안내<br>
- 시공 일정이 잡히면 관리사무실&middot;경비실에 공사 일정 통보<br>
</p>
<p><i class="xi-key"></i> <strong>주부님 TIP!</strong><br>
- "우리 집 창문이 어떤 구조인지, 가구 배치가 창문과 겹치는 부분이 있는지" 미리 확인해 보시면 좋아요!</p>
<p>&nbsp;</p>
<p><font color="#3366cc"><strong>4. 창호 견적 진행 </strong></font>
<div style="height: 1px; background-color: #eee;"></div>
</p>
<p><i class="xi-check-square"></i> <strong>주부님</strong> : 실측 후 창호리모델링 견적을 주시는 건가요?<br>
<i class="xi-check-square"></i> <strong><font color="#ff6600">Hi창호</font></strong><br>
상담이 끝나면, ‘창호 전문 상담가’가 견적서를 만들어서 고객님께 전달 또는 홈페이지에서 확인이 가능합니다. 이때 견적서는 다양한 브랜드로 여러 개의 견적서를 받아 보실 수 있습니다. 여기에 같은 브랜드에 다른 견적도 받아 보실 수 있습니다. 이후에 창호리모델링을 결정하시면 창호 제작 및 시공이 진행됩니다.<br>
시공을 진행하기로 결정하셨다면, 창호 제작 기간과 설치 날짜를 안내해 드릴 거예요.</p>
<p><i class="xi-view-list"></i> <font color="#000000">시공을 결정하셨다면 주부님이 준비할 것</font><br>
- 창문 주변 물건을 미리 치워두면 더 빠르고 안전한 시공이 가능해요<br>
- 시공 당일 먼지가 날릴 수 있으니, 예민한 물건(옷, 침구류 등)은 덮어두는 것도 좋아요.<br>
- 공사일에 관리사무소에서 공사 일정 승인이 필요한 경우, 미리 확인해 주세요.<br>
</p>
<p><i class="xi-key"></i> <strong>주부님 TIP!</strong><br>
- 공사일에 집에 계시는 게 가장 좋아요! 혹시 일정이 안 맞는다면 가족분께 맡겨주셔도 됩니다.</p>
<p>&nbsp;</p>
<p><font color="#3366cc"><strong>5. 창문 설치 후, 마무리 및 A/S 안내</strong></font>
<div style="height: 1px; background-color: #eee;"></div>
</p>
<p><i class="xi-check-square"></i> <strong>주부님</strong> : 창문 시공이 끝난 후에는 어떤 일정이 남아 있나요?<br>
<i class="xi-check-square"></i> <strong><font color="#ff6600">Hi창호</font></strong><br>
네! 창문이 제대로 설치되었는지 ‘창호 전문 시공자’와 함께 꼭 확인하셔야 해요. 우선 시공 당일에 시공 완료에 대한 여러가지 사항을 고객님과 함께 체크해 드립니다. 이후에 궁금한 점이 있다면 홈페이지 문의를 통해 글로 답변이 기본 원칙이고 필요에 따라 전화나 방문을 진행합니다.</p>
<p><i class="xi-view-list"></i> <font color="#000000">설치 후 확인할 것</font><br>
- 창문이 부드럽게 열리고 닫히는지 확인하기<br>
- 틈새 없이 밀착되었는지 체크하기<br>
- 손잡이, 잠금장치가 정상적으로 작동하는지 테스트하기<br>
- A/S(사후 서비스) 기간과 보증 범위 확인하기
</p>
<p><i class="xi-key"></i> <strong>주부님 TIP!</strong><br>
- 창문 설치 후 이상이 있으면 바로 이야기해주세요! 사소한 문제도 바로 해결하는 게 중요해요.</p>
<p><i class="xi-check-square"></i> <strong>창호 상담자를 통해 더 안전하고 편리한 창문 교체를!</strong><br>
- 창문 교체를 고민하고 계신다면, 창호 상담자와 충분히 상담해보세요!<br>
- 단순히 창문을 교체하는 게 아니라, 우리 집에 딱 맞는 창문을 찾고 안전하게 설치하는 과정이에요.</p>
<p><i class="xi-check-square"></i> <strong>궁금한 점이 있다면 언제든지 문의하세요! </strong></p>
</div>
</div>
<!-- content //-->
<p>&nbsp;</p>
<p>&nbsp;</p>
<!-- <div class="doc-cnt">
<div class="link-group">
<div class="col">
<a href="/sub01_3.php"><i class="xi-arrow-left"></i> <strong>창호 전문 상담가 바로가기</strong></a>
</div>
<div class="col">
<a href="/sub02.php"><strong>창호견적 바로가기</strong> <i class="xi-arrow-right"></i></a>
</div>
</div>
</div> -->
</div>
</div>
</div>
<?php
include_once(G5_PATH.'/tail.php');
?>
@@ -0,0 +1,81 @@
<?php
if (!defined('_INDEX_')) define('_INDEX_', true);
//if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가
// 1. 그누보드 공통 파일을 포함하여 기본 설정을 불러옵니다.
include_once '../../../../common.php';
// 2. 💡 [핵심] 우리가 만든 서브페이지 전용 CSS 파일을 동적으로 불러옵니다.
// 이 코드는 반드시 head.php를 포함하기 전에 위치해야 합니다.
add_stylesheet('<link rel="stylesheet" href="'.G5_THEME_URL.'/rb.custom/submenu/css/subpage.css?ver='.G5_SERVER_TIME.'">', 0);
// 3. 💡 [핵심] 그누보드 원본 head.php 대신, 현재 '적용된 테마'의 head.php를 불러옵니다.
// 이렇게 해야 사이트의 공통 헤더, 메뉴, 전체 스타일이 적용됩니다.
include_once(G5_THEME_PATH.'/head.php');
?>
<div id="container" class="sub-page">
<div id="contArea">
<div class="sub-title">
<h2>Hi창호는?</h2>
</div>
<div class="real-cont">
<div class="greetings">
<!-- 💡 [삭제] <style> 태그는 subpage.css 파일로 통합되었으므로 삭제합니다. -->
<!-- *** 모바일 *** -->
<?php if (is_mobile()) { ?>
<div class="row">
<div class="col-lg-6 col-sm-6">
<!-- 💡 [수정] 이미지 경로를 테마 URL 기준으로 변경합니다. -->
<center><img src="<?php echo G5_THEME_URL; ?>/img/sub/sub/ksj.jpg" alt="Hi창호 소개 이미지"></center>
</div>
<div class="col-lg-6 col-sm-6">
<p class="t2">
<center>Hi 창호는 창호 교체를 쉽고 빠르게 도와주는 플랫폼입니다.<br>
한 번에 여러 브랜드 견적을 비교하고 최적의 선택이 가능합니다.<br>
제작부터 시공까지 믿을 수 있는 원스톱 서비스를 제공합니다.
</center>
</p>
</div>
</div>
<?php } ?>
<!-- *** PC *** -->
<?php if (!is_mobile()) { ?>
<div class="heading">
<div class="img"><img src="<?php echo G5_THEME_URL; ?>/img/sub/sub/ksj.jpg" alt="Hi창호 소개 이미지"></div>
<div class="txt">
<p class="t2">Hi 창호는 창호 교체를 쉽고 빠르게 도와주는 플랫폼입니다.<br>
한 번에 여러 브랜드 견적을 비교하고 최적의 선택이 가능합니다.<br>
제작부터 시공까지 믿을 수 있는 원스톱 서비스를 제공합니다.</p>
</div>
</div>
<?php } ?>
<div class="content">
<p>안녕하세요, Hi 창호입니다.<br>
고객의 삶의 질을 높이는 창호 교체, 이제 Hi 창호와 함께 새롭게 경험해보세요.</p>
<p>Hi 창호는 아파트, 연립주택, 빌라, 전원주택 등 다양한 주거 형태에 맞춘 창호 교체를 전문적으로 지원하는 토탈 플랫폼입니다.<br>
기존의 복잡하고 번거로운 견적 요청 과정을 획기적으로 개선하여, 단 한 번의 정보 입력만으로 여러 브랜드의 견적을 손쉽게 비교하고, 고객에게 가장 합리적인 선택지를
제안합니다.</p>
<p>저희는 창호 제작 공장에서 제품을 직접 배송하고, 숙련된 전문 시공팀이 정확하고 깔끔하게 설치까지 책임지는 <strong>원스톱 서비스</strong>를 제공합니다.<br>
단순한 교체를 넘어, 고객의 주거 환경과 예산, 취향을 반영한 <strong>맞춤형 솔루션</strong>을 통해 차별화된 품질과 만족을 약속드립니다.</p>
<p>Hi 창호는 투명한 가격, 고품질 제품, 그리고 믿을 수 있는 시공으로 고객 한 분 한 분의 소중한 공간을 더욱 가치 있게
만듭니다.<br>
편리함과 신뢰, 그리고 완성도 높은 결과까지. 창호 교체의 새로운 기준, Hi 창호가 만들어갑니다.</p>
<p>감사합니다.</p>
</div>
</div>
</div>
</div>
</div>
<?php
include_once(G5_THEME_PATH.'/tail.php');
+874
View File
@@ -0,0 +1,874 @@
<script>location.href = '/bbs/board.php?bo_table=order';</script>
<?php
include_once './_common.php';
include_once G5_LIB_PATH . '/thumbnail.lib.php';
include_once G5_PATH . '/head.php';
?>
<!-- for mobile -->
<div id="menu" class="mobile-navigation">
<div class="home"><a href="/"><img src="/theme/rd.lwd/img/sub/common/logo.png" alt="하이창호"></a></div>
<nav class="nav-menu"></nav>
<a href="javascript:void(0);" class="close">닫기</a>
</div>
<div class="mobile-overlay"></div>
<div class="sub-visual bg2">
<div class="inner">
<h2>Hi 고객스토리</h2>
</div>
</div>
<div class="lnb-wrap">
<div class="lnb">
<div class="swiper-container">
<ul class="swiper-wrapper">
<li class="swiper-slide active "><a href="/sub02.php">창호견적</a></li>
<li class="swiper-slide"><a href="/sub02_1.php">시공견적</a></li>
<li class="swiper-slide "><a href="/sub02_2.php">창호소비효율등급제</a></li>
<li class="swiper-slide "><a href="/sub02_3.php">창호리모델링시대</a></li>
<li class="swiper-slide "><a href="/sub02_4.php">고객이 알아야 할 창호시공 10선</a></li>
</ul>
</div>
</div>
</div>
<div id="container">
<div id="contArea">
<div class="sub-title">
<h2>창호견적</h2>
</div>
<div class="real-cont">
<!--// content -->
<div class="greetings">
<style>
/*** 이실장님 제공소스 ***/
@media screen and (max-width: 992px) {
.row {
display: flex;
flex-wrap: wrap;
margin: 0 auto;
width: 100%;
flex-direction: column;}
}
</style>
<!-- 입력폼 style -->
<style>
.form-group {
margin-bottom: 20px;
padding-bottom: 20px;
border-bottom: 0px solid #eee;
}
label {
font-weight: bold;
display: block;
margin-bottom: 8px;
}
input[type="text"],
textarea {
width: 100%;
padding: 8px;
font-size: 1em;
border: 1px solid #ccc;
border-radius: 4px;
}
.options {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 8px;
}
.options label {
display: flex;
align-items: center;
background: #f9f9f9;
border: 1px solid #ccc;
border-radius: 20px;
padding: 6px 12px;
cursor: pointer;
}
.options input[type="radio"] {
margin-right: 6px;
}
.sub-text {
font-size: 0.9em;
color: #666;
margin-top: 5px;
}
.image-box {
display: flex;
gap: 20px;
margin-top: 10px;
}
.image-box img {
width: 150px;
border: 1px solid #ccc;
border-radius: 8px;
}
.input-inline {
display: flex;
gap: 10px;
flex-wrap: wrap;
align-items: center;
}
.placeholder-input {
color: #aaa;
}
@media (max-width: 600px) {
.options,
.image-box,
.input-inline {
flex-direction: column;
align-items: flex-start;
}
}
input[type="checkbox"] {
width: 20px;
height: 20px;
appearance: none; /* 브라우저 기본 체크박스 제거 */
-webkit-appearance: none;
border: 3px solid #000000; /* 진한 테두리 */
border-radius: 3px;
background-color: white;
cursor: pointer;
display: inline-block;
position: relative;
vertical-align: middle;
}
input[type="checkbox"]:checked::before {
content: '✔';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -55%); /* 정확히 중앙으로 이동 */
font-size: 16px;
color: #990000;
line-height: 1;
}
</style>
<!-- //입력폼 style -->
<div class="content">
<!-------------------------------- 입력폼 시작 -------------------------------->
<form action="/submit" method="post">
<div class="row">
<div class="col-lg-12">
<div class="form-group">
<label>1. 주소</label>
<input type="text" placeholder="주소를 입력하세요">
</div>
</div>
</div>
<div class="row">
<div class="col-lg-2">
<div class="form-group">
<label>2. 평형</label>
<input type="text" placeholder="평형 입력" style="width: 100px;">
</div>
</div>
<div class="col-lg-6">
<div class="form-group">
<label>3. 집의 형태</label>
<div class="options">
<label><input type="radio" name="house-type"> 아파트</label>
<label><input type="radio" name="house-type"> 빌라</label>
<label><input type="radio" name="house-type"> 신축</label>
<label><input type="radio" name="house-type"> 전원주택</label>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="form-group">
<label>4. 창 재질</label>
<div class="options">
<label><input type="radio" name="material"> PVC(폴리염화비닐)</label>
<label><input type="radio" name="material"> AL(알루미늄)</label>
</div>
</div>
</div>
</div>
<!-- 제품선택 --------------------------------->
<div class="row">
<div class="col-lg-12">
<div class="well3">
<font color="#cc0000"><i class="xi-touch"></i> <strong>5. 아래의 선택은 복수 선택이
가능합니다!</strong></font>
<!-- 제품선택영역 -->
<div class="row">
<div class="col-lg-6 col-sm-6">
<div class="form-group">
<center><img src="../images/sub/sub02_01.jpg" alt=""></center>
</div>
</div>
<div class="col-lg-6 col-sm-6">
<div class="well2">
<div class="form-group">
<label><input type="checkbox" name="replace"> <font color="#3366cc"><span
style="font-size: 21px;">거실 발코니1</span></font></label>
<div class="options">
■ 기존창 규격 :
<input type="text" placeholder="가로" style="width: 100px;"><span>X</span>
<input type="text" placeholder="세로" style="width: 100px;">
</div>
<div class="options">
■ 창비율 :
<label><input type="radio" name="ratio"> 1 대 1</label>
<label><input type="radio" name="ratio"> 1 대 1.5 대 1</label>
</div>
<div class="options">
■ 창호형태 :
<label><input type="radio" name="ratio"> 단창</label>
<label><input type="radio" name="ratio"> 이중창</label>
<label><input type="radio" name="ratio"> 분합창</label>
<label><input type="radio" name="ratio"> 발코니창</label>
※ 단창은 창짝이 2개, 이중창은 창짝이 4개<br>
※ 분합창 : 거실과 베란다 사이의 문<br>
※ 발코니창 : 베란다 외부와 접한 창
</div>
<div class="options">
■ 방충망 :
<label><input type="radio" name="ratio"> 유</label>
<label><input type="radio" name="ratio"> 무</label>
※ 방충망은 외부에 접한 외창에 기본설치<br>
※ 내창은 고객 선택
</div>
<!-- <div class="sub-text">
※ 내창: 외부에 접하지 않은 내부의 창<br>
※ 외창: 외부에 접한 창
</div> -->
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6 col-sm-6">
<div class="well2">
<div class="form-group">
<label><input type="checkbox" name="replace"> <font color="#3366cc"><span
style="font-size: 21px;">거실 발코니2</span></font></label>
<div class="options">
■ 기존창 규격 :
<input type="text" placeholder="가로" style="width: 100px;"><span>X</span>
<input type="text" placeholder="세로" style="width: 100px;">
</div>
<div class="options">
■ 창비율 :
<label><input type="radio" name="ratio"> 1 대 1</label>
<label><input type="radio" name="ratio"> 1 대 1.5 대 1</label>
</div>
<div class="options">
■ 창호형태 :
<label><input type="radio" name="ratio"> 단창</label>
<label><input type="radio" name="ratio"> 이중창</label>
<label><input type="radio" name="ratio"> 분합창</label>
<label><input type="radio" name="ratio"> 발코니창</label>
</div>
<div class="options">
■ 방충망 :
<label><input type="radio" name="ratio"> 유</label>
<label><input type="radio" name="ratio"> 무</label>
</div>
</div>
</div>
</div>
<div class="col-lg-6 col-sm-6">
<div class="well2">
<div class="form-group">
<label><input type="checkbox" name="replace"> <font color="#006600"><span
style="font-size: 21px;">안방 발코니1</span></font></label>
<div class="options">
■ 기존창 규격 :
<input type="text" placeholder="가로" style="width: 100px;"><span>X</span>
<input type="text" placeholder="세로" style="width: 100px;">
</div>
<div class="options">
■ 창비율 :
<label><input type="radio" name="ratio"> 1 대 1</label>
<label><input type="radio" name="ratio"> 1 대 1.5 대 1</label>
</div>
<div class="options">
■ 창호형태 :
<label><input type="radio" name="ratio"> 단창</label>
<label><input type="radio" name="ratio"> 이중창</label>
<label><input type="radio" name="ratio"> 분합창</label>
<label><input type="radio" name="ratio"> 발코니창</label>
</div>
<div class="options">
■ 방충망 :
<label><input type="radio" name="ratio"> 유</label>
<label><input type="radio" name="ratio"> 무</label>
</div>
</div>
</div>
</div>
<div class="col-lg-6 col-sm-6">
<div class="well2">
<div class="form-group">
<label><input type="checkbox" name="replace"> <font color="#006600"><span
style="font-size: 21px;">안방 발코니2</span></font></label>
<div class="options">
■ 기존창 규격 :
<input type="text" placeholder="가로" style="width: 100px;"><span>X</span>
<input type="text" placeholder="세로" style="width: 100px;">
</div>
<div class="options">
■ 창비율 :
<label><input type="radio" name="ratio"> 1 대 1</label>
<label><input type="radio" name="ratio"> 1 대 1.5 대 1</label>
</div>
<div class="options">
■ 창호형태 :
<label><input type="radio" name="ratio"> 단창</label>
<label><input type="radio" name="ratio"> 이중창</label>
<label><input type="radio" name="ratio"> 분합창</label>
<label><input type="radio" name="ratio"> 발코니창</label>
</div>
<div class="options">
■ 방충망 :
<label><input type="radio" name="ratio"> 유</label>
<label><input type="radio" name="ratio"> 무</label>
</div>
</div>
</div>
</div>
<div class="col-lg-6 col-sm-6">
<div class="well2">
<div class="form-group">
<label><input type="checkbox" name="replace"> <font color="#ff6600"><span
style="font-size: 21px;">뒤베란다 외부창1</span></font></label>
<div class="options">
■ 기존창 규격 :
<input type="text" placeholder="가로" style="width: 100px;"><span>X</span>
<input type="text" placeholder="세로" style="width: 100px;">
</div>
<div class="options">
■ 창비율 :
<label><input type="radio" name="ratio"> 1 대 1</label>
<label><input type="radio" name="ratio"> 1 대 1.5 대 1</label>
</div>
<div class="options">
■ 창호형태 :
<label><input type="radio" name="ratio"> 단창</label>
<label><input type="radio" name="ratio"> 이중창</label>
<label><input type="radio" name="ratio"> 분합창</label>
<label><input type="radio" name="ratio"> 발코니창</label>
</div>
<div class="options">
■ 방충망 :
<label><input type="radio" name="ratio"> 유</label>
<label><input type="radio" name="ratio"> 무</label>
</div>
</div>
</div>
</div>
<div class="col-lg-6 col-sm-6">
<div class="well2">
<div class="form-group">
<label><input type="checkbox" name="replace"> <font color="#ff6600"><span
style="font-size: 21px;">뒤베란다 외부창2</span></font></label>
<div class="options">
■ 기존창 규격 :
<input type="text" placeholder="가로" style="width: 100px;"><span>X</span>
<input type="text" placeholder="세로" style="width: 100px;">
</div>
<div class="options">
■ 창비율 :
<label><input type="radio" name="ratio"> 1 대 1</label>
<label><input type="radio" name="ratio"> 1 대 1.5 대 1</label>
</div>
<div class="options">
■ 창호형태 :
<label><input type="radio" name="ratio"> 단창</label>
<label><input type="radio" name="ratio"> 이중창</label>
<label><input type="radio" name="ratio"> 분합창</label>
<label><input type="radio" name="ratio"> 발코니창</label>
</div>
<div class="options">
■ 방충망 :
<label><input type="radio" name="ratio"> 유</label>
<label><input type="radio" name="ratio"> 무</label>
</div>
</div>
</div>
</div>
<div class="col-lg-6 col-sm-6">
<div class="well2">
<div class="form-group">
<label><input type="checkbox" name="replace"> <font color="#ff6600"><span
style="font-size: 21px;">뒤베란다 외부창3</span></font></label>
<div class="options">
■ 기존창 규격 :
<input type="text" placeholder="가로" style="width: 100px;"><span>X</span>
<input type="text" placeholder="세로" style="width: 100px;">
</div>
<div class="options">
■ 창비율 :
<label><input type="radio" name="ratio"> 1 대 1</label>
<label><input type="radio" name="ratio"> 1 대 1.5 대 1</label>
</div>
<div class="options">
■ 창호형태 :
<label><input type="radio" name="ratio"> 단창</label>
<label><input type="radio" name="ratio"> 이중창</label>
<label><input type="radio" name="ratio"> 분합창</label>
<label><input type="radio" name="ratio"> 발코니창</label>
</div>
<div class="options">
■ 방충망 :
<label><input type="radio" name="ratio"> 유</label>
<label><input type="radio" name="ratio"> 무</label>
</div>
</div>
</div>
</div>
<div class="col-lg-6 col-sm-6">
<div class="well2">
<div class="form-group">
<label><input type="checkbox" name="replace"> <font color="#990000"><span
style="font-size: 21px;">다용도실 외부창</span></font></label>
<div class="options">
■ 기존창 규격 :
<input type="text" placeholder="가로" style="width: 100px;"><span>X</span>
<input type="text" placeholder="세로" style="width: 100px;">
</div>
<div class="options">
■ 창비율 :
<label><input type="radio" name="ratio"> 1 대 1</label>
<label><input type="radio" name="ratio"> 1 대 1.5 대 1</label>
</div>
<div class="options">
■ 창호형태 :
<label><input type="radio" name="ratio"> 단창</label>
<label><input type="radio" name="ratio"> 이중창</label>
<label><input type="radio" name="ratio"> 분합창</label>
<label><input type="radio" name="ratio"> 발코니창</label>
</div>
<div class="options">
■ 방충망 :
<label><input type="radio" name="ratio"> 유</label>
<label><input type="radio" name="ratio"> 무</label>
</div>
</div>
</div>
</div>
<div class="col-lg-6 col-sm-6">
<div class="well2">
<div class="form-group">
<label><input type="checkbox" name="replace"> <font color="#990000"><span
style="font-size: 21px;">다용도실 내부창</span></font></label>
<div class="options">
■ 기존창 규격 :
<input type="text" placeholder="가로" style="width: 100px;"><span>X</span>
<input type="text" placeholder="세로" style="width: 100px;">
</div>
<div class="options">
■ 창비율 :
<label><input type="radio" name="ratio"> 1 대 1</label>
<label><input type="radio" name="ratio"> 1 대 1.5 대 1</label>
</div>
<div class="options">
■ 창호형태 :
<label><input type="radio" name="ratio"> 단창</label>
<label><input type="radio" name="ratio"> 이중창</label>
<label><input type="radio" name="ratio"> 분합창</label>
<label><input type="radio" name="ratio"> 발코니창</label>
</div>
<div class="options">
■ 방충망 :
<label><input type="radio" name="ratio"> 유</label>
<label><input type="radio" name="ratio"> 무</label>
</div>
</div>
</div>
</div>
<div class="col-lg-6 col-sm-6">
<div class="well2">
<div class="form-group">
<label><input type="checkbox" name="replace"> <font color="#000000"><span
style="font-size: 21px;">안방 내창</span></font></label>
<div class="options">
■ 기존창 규격 :
<input type="text" placeholder="가로" style="width: 100px;"><span>X</span>
<input type="text" placeholder="세로" style="width: 100px;">
</div>
<div class="options">
■ 창비율 :
<label><input type="radio" name="ratio"> 1 대 1</label>
<label><input type="radio" name="ratio"> 1 대 1.5 대 1</label>
</div>
<div class="options">
■ 창호형태 :
<label><input type="radio" name="ratio"> 단창</label>
<label><input type="radio" name="ratio"> 이중창</label>
<label><input type="radio" name="ratio"> 분합창</label>
<label><input type="radio" name="ratio"> 발코니창</label>
</div>
<div class="options">
■ 방충망 :
<label><input type="radio" name="ratio"> 유</label>
<label><input type="radio" name="ratio"> 무</label>
</div>
</div>
</div>
</div>
<div class="col-lg-6 col-sm-6">
<div class="well2">
<div class="form-group">
<label><input type="checkbox" name="replace"> <font color="#000000"><span
style="font-size: 21px;">거실 분합 내창</span></font></label>
<div class="options">
■ 기존창 규격 :
<input type="text" placeholder="가로" style="width: 100px;"><span>X</span>
<input type="text" placeholder="세로" style="width: 100px;">
</div>
<div class="options">
■ 창비율 :
<label><input type="radio" name="ratio"> 1 대 1</label>
<label><input type="radio" name="ratio"> 1 대 1.5 대 1</label>
</div>
<div class="options">
■ 창호형태 :
<label><input type="radio" name="ratio"> 단창</label>
<label><input type="radio" name="ratio"> 이중창</label>
<label><input type="radio" name="ratio"> 분합창</label>
<label><input type="radio" name="ratio"> 발코니창</label>
</div>
<div class="options">
■ 방충망 :
<label><input type="radio" name="ratio"> 유</label>
<label><input type="radio" name="ratio"> 무</label>
</div>
</div>
</div>
</div>
<div class="col-lg-6 col-sm-6">
<div class="well2">
<div class="form-group">
<label><input type="checkbox" name="replace"> <font color="#000000"><span
style="font-size: 21px;">주방 터닝도어 내창</span></font></label>
<div class="options">
■ 기존창 규격 :
<input type="text" placeholder="가로" style="width: 100px;"><span>X</span>
<input type="text" placeholder="세로" style="width: 100px;">
</div>
<div class="options">
■ 창비율 :
<label><input type="radio" name="ratio"> 1 대 1</label>
<label><input type="radio" name="ratio"> 1 대 1.5 대 1</label>
</div>
<div class="options">
■ 창호형태 :
<label><input type="radio" name="ratio"> 단창</label>
<label><input type="radio" name="ratio"> 이중창</label>
<label><input type="radio" name="ratio"> 분합창</label>
<label><input type="radio" name="ratio"> 발코니창</label>
</div>
<div class="options">
■ 방충망 :
<label><input type="radio" name="ratio"> 유</label>
<label><input type="radio" name="ratio"> 무</label>
</div>
</div>
</div>
</div>
<div class="col-lg-6 col-sm-6">
<div class="well2">
<div class="form-group">
<label><input type="checkbox" name="replace"> <font color="#000000"><span
style="font-size: 21px;">입구방</span></font></label>
<div class="options">
■ 기존창 규격 :
<input type="text" placeholder="가로" style="width: 100px;"><span>X</span>
<input type="text" placeholder="세로" style="width: 100px;">
</div>
<div class="options">
■ 창비율 :
<label><input type="radio" name="ratio"> 1 대 1</label>
<label><input type="radio" name="ratio"> 1 대 1.5 대 1</label>
</div>
<div class="options">
■ 창호형태 :
<label><input type="radio" name="ratio"> 단창</label>
<label><input type="radio" name="ratio"> 이중창</label>
<label><input type="radio" name="ratio"> 분합창</label>
<label><input type="radio" name="ratio"> 발코니창</label>
</div>
<div class="options">
■ 방충망 :
<label><input type="radio" name="ratio"> 유</label>
<label><input type="radio" name="ratio"> 무</label>
</div>
</div>
</div>
</div>
<div class="col-lg-6 col-sm-6">
<div class="well2">
<div class="form-group">
<label><input type="checkbox" name="replace"> <font color="#000000"><span
style="font-size: 21px;">다용도실문 내창</span></font></label>
<div class="options">
■ 기존창 규격 :
<input type="text" placeholder="가로" style="width: 100px;"><span>X</span>
<input type="text" placeholder="세로" style="width: 100px;">
</div>
<div class="options">
■ 창비율 :
<label><input type="radio" name="ratio"> 1 대 1</label>
<label><input type="radio" name="ratio"> 1 대 1.5 대 1</label>
</div>
<div class="options">
■ 창호형태 :
<label><input type="radio" name="ratio"> 단창</label>
<label><input type="radio" name="ratio"> 이중창</label>
<label><input type="radio" name="ratio"> 분합창</label>
<label><input type="radio" name="ratio"> 발코니창</label>
</div>
<div class="options">
■ 방충망 :
<label><input type="radio" name="ratio"> 유</label>
<label><input type="radio" name="ratio"> 무</label>
</div>
</div>
</div>
</div>
<div class="col-lg-6 col-sm-6">
<div class="well2">
<div class="form-group">
<label><input type="checkbox" name="replace"> <font color="#000000"><span
style="font-size: 21px;">건너방 분합문 내창</span></font></label>
<div class="options">
■ 기존창 규격 :
<input type="text" placeholder="가로" style="width: 100px;"><span>X</span>
<input type="text" placeholder="세로" style="width: 100px;">
</div>
<div class="options">
■ 창비율 :
<label><input type="radio" name="ratio"> 1 대 1</label>
<label><input type="radio" name="ratio"> 1 대 1.5 대 1</label>
</div>
<div class="options">
■ 창호형태 :
<label><input type="radio" name="ratio"> 단창</label>
<label><input type="radio" name="ratio"> 이중창</label>
<label><input type="radio" name="ratio"> 분합창</label>
<label><input type="radio" name="ratio"> 발코니창</label>
</div>
<div class="options">
■ 방충망 :
<label><input type="radio" name="ratio"> 유</label>
<label><input type="radio" name="ratio"> 무</label>
</div>
</div>
</div>
</div>
<div class="col-lg-6 col-sm-6">
<div class="well2">
<div class="form-group">
<label><input type="checkbox" name="replace"> <font color="#000000"><span
style="font-size: 21px;">안방 분합문</span></font></label>
<div class="options">
■ 기존창 규격 :
<input type="text" placeholder="가로" style="width: 100px;"><span>X</span>
<input type="text" placeholder="세로" style="width: 100px;">
</div>
<div class="options">
■ 창비율 :
<label><input type="radio" name="ratio"> 1 대 1</label>
<label><input type="radio" name="ratio"> 1 대 1.5 대 1</label>
</div>
<div class="options">
■ 창호형태 :
<label><input type="radio" name="ratio"> 단창</label>
<label><input type="radio" name="ratio"> 이중창</label>
<label><input type="radio" name="ratio"> 분합창</label>
<label><input type="radio" name="ratio"> 발코니창</label>
</div>
<div class="options">
■ 방충망 :
<label><input type="radio" name="ratio"> 유</label>
<label><input type="radio" name="ratio"> 무</label>
</div>
</div>
</div>
</div>
<div class="col-lg-6 col-sm-6">
<div class="well2">
<div class="form-group">
<label><input type="checkbox" name="replace"> <font color="#000000"><span
style="font-size: 21px;">주방창</span></font></label>
<div class="options">
■ 기존창 규격 :
<input type="text" placeholder="가로" style="width: 100px;"><span>X</span>
<input type="text" placeholder="세로" style="width: 100px;">
</div>
<div class="options">
■ 창비율 :
<label><input type="radio" name="ratio"> 1 대 1</label>
<label><input type="radio" name="ratio"> 1 대 1.5 대 1</label>
</div>
<div class="options">
■ 창호형태 :
<label><input type="radio" name="ratio"> 단창</label>
<label><input type="radio" name="ratio"> 이중창</label>
<label><input type="radio" name="ratio"> 분합창</label>
<label><input type="radio" name="ratio"> 발코니창</label>
</div>
<div class="options">
■ 방충망 :
<label><input type="radio" name="ratio"> 유</label>
<label><input type="radio" name="ratio"> 무</label>
</div>
</div>
</div>
</div>
</div>
<!-- //제품선택영역 -->
</div>
</div>
<!-- //제품선택 끝 ----------------------------->
<div class="row">
<div class="col-lg-4">
<div class="form-group">
<label>6. 창호색상</label>
<div class="options">
<label><input type="radio" name="color"> 일반 백색</label>
<label><input type="radio" name="color"> 도장(색지정)</label>
</div>
<div class="sub-text">
※ 도장 - 창호는 기본 백색으로 생산하지만 고객이 지정색을 원할 경우 색을 입히는 비용 추가
</div>
</div>
</div>
<div class="col-lg-4">
<div class="form-group">
<label>7. 교체부위</label>
<div class="options">
<label><input type="radio" name="glass"> 전체</label>
<label><input type="radio" name="glass"> 외창</label>
<label><input type="radio" name="glass"> 내창</label>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="form-group">
<label>8. 핸들사양</label>
<div class="options">
<label><input type="radio" name="handle"> 자동</label>
<label><input type="radio" name="handle"> 일반</label>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-4">
<div class="form-group">
<label>9. 유리사양</label>
<div class="options">
<label><input type="radio" name="glass"> 로이</label>
<label><input type="radio" name="glass"> 일반</label>
</div>
<div class="sub-text">
※ 로이유리 - 일반 유리 표면에 단열 코팅
</div>
</div>
</div>
<div class="col-lg-4">
<div class="form-group">
<label>10. 유리색상</label>
<div class="options">
<label><input type="radio" name="glass-color"> 투명</label>
<label><input type="radio" name="glass-color"> 그린</label>
<label><input type="radio" name="glass-color"> 미스트(불투명)</label>
<input type="text" placeholder="기타" onfocus="this.placeholder=''"
onblur="this.placeholder='기타'">
</div>
</div>
</div>
<div class="col-lg-4">
<div class="form-group">
<label>11. 추가사항</label>
<textarea rows="5" placeholder="추가로 필요한 내용을 입력해 주세요"></textarea>
</div>
</div>
</div>
<!-- 버튼 영역 -->
<div class="row">
<div class="col-lg-12">
<div style="margin-top: 20px;">
<center>
<button type="submit">보내기</button>
<button type="button" onclick="history.back();">취소</button>
</center>
</div>
</div>
</div>
<style>
button {
padding: 12px 30px; /* 높이와 너비 늘림 */
font-size: 16px; /* 글자 크기 키움 */
margin: 0 10px; /* 버튼 사이 여백 */
border: none;
border-radius: 6px;
cursor: pointer;
}
button[type="submit"] {
background-color: #4CAF50;
color: white;
}
button[type="button"] {
background-color: #f44336;
color: white;
}
button:hover {
opacity: 0.9;
}
</style>
</form>
<!-------------------------------- //입력폼 시작 -------------------------------->
<p>&nbsp;</p>
</div>
</div>
<!-- content //-->
</div>
</div>
</div>
<?php
include_once(G5_PATH . '/tail.php');
?>
@@ -0,0 +1,197 @@
<?php
include_once('./_common.php');
// 페이지 제목 설정
$g5['title'] = '시공견적 안내';
// 헤더 파일 포함
include_once(G5_PATH.'/head.php');
?>
<!-- for mobile -->
<div id="menu" class="mobile-navigation">
<div class="home"><a href="/"><img src="/theme/rd.lwd/img/sub/common/logo.png" alt="하이창호"></a></div>
<nav class="nav-menu"></nav>
<a href="javascript:void(0);" class="close">닫기</a>
</div>
<div class="mobile-overlay"></div>
<div class="sub-visual bg2">
<div class="inner">
<h2>Hi 고객스토리</h2>
</div>
</div>
<div class="lnb-wrap">
<div class="lnb">
<div class="swiper-container">
<ul class="swiper-wrapper">
<li class="swiper-slide"><a href="/sub02.php">창호견적</a></li>
<li class="swiper-slide active"><a href="/sub02_1.php">시공견적</a></li>
<li class="swiper-slide"><a href="/sub02_2.php">창호소비효율등급제</a></li>
<li class="swiper-slide"><a href="/sub02_3.php">창호리모델링시대</a></li>
<li class="swiper-slide"><a href="/sub02_4.php">고객이 알아야 할 창호시공 10선</a></li>
</ul>
</div>
</div>
</div>
<div id="container">
<div id="contArea">
<div class="sub-title">
<h2>시공견적 페이지</h2>
</div>
<div class="real-cont">
<!--// content -->
<div class="greetings">
<style>
/* 페이지 전용 스타일 */
.estimate-guide-content h3.doc-tit { font-size: 22px; color: #003300; margin-bottom: 20px; }
.estimate-guide-content p { margin-bottom: 15px; color: #555; line-height: 1.8; }
.variable-list { list-style: none; padding-left: 20px; margin-bottom: 20px; }
.variable-list li { position: relative; margin-bottom: 10px; padding-left: 5px; }
.variable-list li:before { content: '✓'; position: absolute; left: -20px; color: #3d6cb9; font-weight: bold; }
.important-notice { background-color: #fffbe6; border: 1px solid #ffe58f; border-radius: 8px; padding: 15px 20px; color: #8a6d3b; margin-top: 20px; }
.important-notice i { margin-right: 10px; }
.cost-table { width: 100%; border-collapse: collapse; margin-top: 20px; font-size: 15px; }
.cost-table th, .cost-table td { border: 1px solid #ddd; padding: 12px; text-align: left; }
.cost-table th { background-color: #f9f9f9; font-weight: bold; }
.cost-table td:last-child { text-align: right; font-family: 'Malgun Gothic', sans-serif; }
.calculator-box { background-color: #f5faff; border: 1px solid #d6eaff; border-radius: 8px; padding: 25px; margin-top: 20px; }
.calculator-box .input-group { display: flex; align-items: center; margin-bottom: 15px; }
.calculator-box label { width: 100px; font-weight: bold; }
.calculator-box input[type="number"] { flex-grow: 1; padding: 10px; border: 1px solid #ccc; border-radius: 4px; }
.calculator-box button { display: block; width: 100%; padding: 12px; background-color: #3d6cb9; color: white; border: none; border-radius: 4px; font-size: 18px; cursor: pointer; transition: background-color 0.2s; }
.calculator-box button:hover { background-color: #2c5282; }
#result-display { margin-top: 20px; padding: 15px; background-color: #fff; border: 1px solid #ddd; border-radius: 4px; font-size: 18px; text-align: center; }
#result-display span { font-weight: bold; color: #d9534f; }
@media screen and (max-width:992px){
.row { display: flex; flex-wrap: wrap; margin: 0 auto; width: 100%; flex-direction: column; }
}
</style>
<div class="content estimate-guide-content">
<h3 class="doc-tit"><font color="#003300">창호 시공비 안내</font></h3>
<div class="row">
<div class="col-lg-12">
<p>
고객님이 시공을 의뢰할 경우 참고하는 ‘시공견적페이지’입니다. 이곳에서는 창호 시공 진행과정에 따른 금액 변동을 알 수 있습니다.
</p>
<p>
창호 시공 금액이 변동될 수 있는 항목들을 확인하시고 항목들에 따라서 추가되는 금액을 확인하신 후 ‘창호전문상담가’가 추후에 방문해 다시 자세하게 설명해 드립니다.
</p>
<p>
이곳에서는 창호 틀당 고객님이 직접 시공비를 산출하실 수도 있습니다.
</p>
</div>
</div>
<div style="height: 1px; background-color: #eee; margin-bottom: 30px; margin-top: 10px;"></div>
<h3 class="doc-tit"><font color="#003300">창호 시공 시 현장 변동 항목</font></h3>
<div class="row">
<div class="col-lg-12">
<p>
아래는 창호 시공 시 현장 상황에 따라 시공비가 달라질 수 있는 주요 항목들입니다.
</p>
<ul class="variable-list">
<li>층고에 따른 사다리차 가능 유무 (윈치 작업)</li>
<li>하부타일 마감</li>
<li>베란다 확장</li>
<li>상부장 탈부착 (씽크대)</li>
<li>하리(구조물) 절단</li>
<li>엘리베이터 사용 여부</li>
</ul>
<p class="important-notice">
<i class="fa fa-exclamation-triangle"></i>
위의 현장 상황에 따라서 시공비 편차가 많이 발생할 수 있기 때문에, 실제 시공 시에는 <strong>창호전문상담가의 방문을 원칙</strong>으로 하고 있습니다.
</p>
</div>
</div>
<div style="height: 1px; background-color: #eee; margin-bottom: 30px; margin-top: 10px;"></div>
<h3 class="doc-tit"><font color="#003300">주요 추가 비용 항목 (예시)</font></h3>
<div class="row">
<div class="col-lg-12">
<table class="cost-table">
<thead>
<tr>
<th>항목</th>
<th>비용 (VAT 별도)</th>
</tr>
</thead>
<tbody>
<tr><td>스카이 장비</td><td>600,000원</td></tr>
<tr><td>사다리차</td><td>300,000원</td></tr>
<tr><td>윈치</td><td>300,000원</td></tr>
<tr><td>하부타일 마감</td><td>100,000원</td></tr>
<tr><td>몰딩 마감</td><td>100,000원</td></tr>
<tr><td>철거비 (기본 6틀)</td><td>450,000원</td></tr>
<tr><td>추가 철거 (틀당)</td><td>30,000원</td></tr>
<tr><td>보양 작업비</td><td>150,000원</td></tr>
<tr><td>싱크대 상부장 탈부착</td><td>100,000원</td></tr>
</tbody>
</table>
</div>
</div>
<div style="height: 1px; background-color: #eee; margin-bottom: 30px; margin-top: 10px;"></div>
<h3 class="doc-tit"><font color="#003300">창당 시공 단가 간편 산출기</font></h3>
<div class="row">
<div class="col-lg-12">
<p>창호의 가로, 세로 사이즈를 입력하여 대략적인 시공 단가를 계산해볼 수 있습니다. (정확한 금액은 전문가 상담이 필요합니다.)</p>
<div class="calculator-box">
<div class="input-group">
<label for="width-input">가로 (mm)</label>
<input type="number" id="width-input" placeholder="예: 1800">
</div>
<div class="input-group">
<label for="height-input">세로 (mm)</label>
<input type="number" id="height-input" placeholder="예: 2100">
</div>
<button id="calculate-btn">계산하기</button>
<div id="result-display">
예상 시공 단가: <span>계산 버튼을 눌러주세요.</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- content //-->
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const calculateBtn = document.getElementById('calculate-btn');
const widthInput = document.getElementById('width-input');
const heightInput = document.getElementById('height-input');
const resultSpan = document.querySelector('#result-display span');
calculateBtn.addEventListener('click', function() {
const width = parseFloat(widthInput.value);
const height = parseFloat(heightInput.value);
if (isNaN(width) || isNaN(height) || width <= 0 || height <= 0) {
alert('올바른 가로, 세로 값을 mm 단위로 입력해주세요.');
resultSpan.textContent = '계산 오류';
return;
}
// 공식: (가로(mm) * 세로(mm) / 1,000,000) * 90,000원
const areaInM2 = (width * height) / 1000000;
const pricePerM2 = 90000;
const estimatedCost = Math.round(areaInM2 * pricePerM2);
resultSpan.textContent = estimatedCost.toLocaleString('ko-KR') + ' 원';
});
});
</script>
<?php
// 푸터 파일 포함
include_once(G5_PATH.'/tail.php');
?>
@@ -0,0 +1,119 @@
<?php
include_once './_common.php';
include_once G5_LIB_PATH.'/thumbnail.lib.php';
include_once G5_PATH.'/head.php';
?>
<!-- for mobile -->
<div id="menu" class="mobile-navigation">
<div class="home"><a href="/"><img src="/theme/rd.lwd/img/sub/common/logo.png" alt="하이창호"></a></div>
<nav class="nav-menu"></nav>
<a href="javascript:void(0);" class="close">닫기</a>
</div>
<div class="mobile-overlay"></div>
<div class="sub-visual bg2">
<div class="inner">
<h2>Hi 고객스토리</h2>
</div>
</div>
<div class="lnb-wrap">
<div class="lnb">
<div class="swiper-container">
<ul class="swiper-wrapper">
<li class="swiper-slide "><a href="/sub02.php">창호견적</a></li>
<li class="swiper-slide"><a href="/sub02_1.php">시공견적</a></li>
<li class="swiper-slide active "><a href="/sub02_2.php">창호소비효율등급제</a></li>
<li class="swiper-slide "><a href="/sub02_3.php">창호리모델링시대</a></li>
<li class="swiper-slide "><a href="/sub02_4.php">고객이 알아야 할 창호시공 10선</a></li>
</ul>
</div>
</div>
</div>
<div id="container">
<div id="contArea">
<div class="sub-title">
<h2>창호 소비 효율 등급제</h2>
</div>
<div class="real-cont">
<!--// content -->
<div class="greetings">
<style>
/*** 이실장님 제공소스 ***/
@media screen and (max-width:992px){
.row {
display: flex;
flex-wrap: wrap;
margin: 0 auto;
width: 100%;
flex-direction: column;}
}
</style>
<div class="content">
<h3 class="doc-tit"><font color="#003300">창호 소비효율 등급제, 왜 중요할까요?</font></h3>
<div class="row">
<div class="col-lg-5"><center><img class="img-responsive" src="/theme/rd.lwd/img/sub/sub/dn01.jpg" alt=""/></center></div>
<div class="col-lg-7">
<p>요즘 창호를 교체하려고 하면 **‘소비효율 등급’**이라는 단어를 자주 접하게 됩니다.<br>창호에도 냉장고나 세탁기처럼 에너지 등급이 있다는 사실, 알고 계셨나요?<br>이 등급제는 우리 집의 <strong>난방비 절감과 에너지 절약을 돕기 위해 만들어진 제도</strong>입니다.<br>하지만 처음부터 존재했던 것은 아닙니다.<br>창호 소비효율 등급제는 왜 도입되었으며, 어떤 변화가 있었을까요?</p>
</div>
</div>
<div style="height: 1px; background-color: #eee; margin-bottom: 30px; margin-top: 10px;"></div>
<h3 class="doc-tit"><font color="#003300">창호 소비효율 등급제의 도입 배경</font></h3>
<div class="row">
<div class="col-lg-7">
<p>과거에는 창호의 성능을 평가할 수 있는 객관적인 기준이 없었습니다.<br>
소비자들은 단순히 디자인이나 가격을 기준으로 창호를 선택했지만, 사실 창호의 성능 차이는 매우 큽니다. 특히 <strong>열이 잘 새어나가는 창호는 난방비를 증가시키고, 실내 환경을 불안정하게 만드는 원인</strong>이 됩니다.<br>
정부는 에너지 절약을 위해 2012년부터 창호에도 <strong>소비효율 등급제</strong>를 도입했습니다.<br>이 제도를 통해 창호의 단열 성능을 1등급부터 5등급까지 나누고, <strong>소비자가 창호를 비교하고 선택할 수 있도록</strong> 했습니다.<br>즉, 성능이 좋은 창호를 쉽게 구별할 수 있도록 만든 것입니다.</p>
</div>
<div class="col-lg-5"><center><img class="img-responsive" src="/theme/rd.lwd/img/sub/sub/dn02.jpg" alt=""/></center></div>
</div>
<div style="height: 1px; background-color: #eee; margin-bottom: 30px; margin-top: 10px;"></div>
<h3 class="doc-tit"><font color="#003300">창호 1등급 기준이 변경된 이유</font></h3>
<div class="row">
<div class="col-lg-5"><center><img class="img-responsive" src="/theme/rd.lwd/img/sub/sub/dn03.jpg" alt=""/></center></div>
<div class="col-lg-7">
<p>하지만 시간이 지나면서 건축 기술이 발전하고, 창호의 성능도 점점 향상되었습니다.<br>
초기에는 <strong>기준이 낮아</strong> 웬만한 제품이 1등급을 받을 수 있었지만, 이후 <strong>더 엄격한 기준이 필요</strong>해졌습니다.<br>
그래서 2023년부터 <strong>1등급 기준이 더욱 강화</strong>되었습니다.<br>과거 1등급이었던 창호가 이제는 2등급이나 3등급으로 내려간 것이죠. 이는 <strong>보다 높은 성능의 창호가 1등급을 받을 수 있도록 기준을 조정한 것</strong>입니다. 이를 통해 정말 단열이 뛰어난 창호만이 1등급을 받을 수 있게 되었고, 소비자들은 더욱 효율적인 창호를 선택할 수 있게 되었습니다.</p>
</div>
</div>
<div style="height: 1px; background-color: #eee; margin-bottom: 30px; margin-top: 10px;"></div>
<h3 class="doc-tit"><font color="#003300">창호 등급제와 정부 정책의 변화</font></h3>
<div class="row">
<div class="col-lg-12">
<p>창호 소비효율 등급제는 단순한 제품 비교를 위한 것이 아닙니다. <strong>정부의 에너지 절감 정책과도 깊은 관련</strong>이 있습니다.<br>
● 2020년 부터 <strong>그린리모델링 사업</strong>을 통해 단열 성능이 높은 창호로 교체하면 <strong>정부 보조금</strong>을 받을 수 있도록 지원하고 있습니다.<br>
● 2023년 이후, 건물에너지 절감을 위해 신축 건물뿐만 아니라 기존 건물에도 <strong>고효율 창호 설치가 의무화</strong>되는 방향으로 정책이 강화되고 있습니다.<br>
이런 흐름 속에서 창호 소비효율 등급제는 점점 더 중요해지고 있습니다.</p>
</div>
</div>
<div style="height: 1px; background-color: #eee; margin-bottom: 30px; margin-top: 10px;"></div>
<h3 class="doc-tit"><font color="#003300">소비자에게 주는 의미: 더 나은 선택을 위한 기준</font></h3>
<div class="row">
<div class="col-lg-12">
<p>예전에는 창호를 고를 때 가격과 디자인만을 고려했지만, 이제는 <strong>창호 성능을 꼼꼼히 따져봐야 하는 시대</strong>가 되었습니다.<br>
✔ 1등급 창호는 <strong>단열이 뛰어나 난방비 절약</strong> 효과가 크고,<br>
✔ 등급이 낮을수록 실내 온도 유지가 어렵고 에너지 낭비가 심해질 수 있습니다.<br>
창호 소비효율 등급제는 <strong>우리 가족이 보다 쾌적한 환경에서 생활할 수 있도록 돕는 중요한 기준</strong>입니다. 이제 창호를 교체할 때는 반드시 <strong>소비효율 등급을 확인</strong>하고, 현명한 선택을 하세요!</p>
</div>
</div>
</div>
</div>
<!-- content //-->
</div>
</div>
</div>
<?php
include_once(G5_PATH.'/tail.php');
?>
@@ -0,0 +1,103 @@
<?php
include_once './_common.php';
include_once G5_LIB_PATH.'/thumbnail.lib.php';
include_once G5_PATH.'/head.php';
?>
<!-- for mobile -->
<div id="menu" class="mobile-navigation">
<div class="home"><a href="/"><img src="/theme/rd.lwd/img/sub/common/logo.png" alt="하이창호"></a></div>
<nav class="nav-menu"></nav>
<a href="javascript:void(0);" class="close">닫기</a>
</div>
<div class="mobile-overlay"></div>
<div class="sub-visual bg2">
<div class="inner">
<h2>Hi 고객스토리</h2>
</div>
</div>
<div class="lnb-wrap">
<div class="lnb">
<div class="swiper-container">
<ul class="swiper-wrapper">
<li class="swiper-slide "><a href="/sub02.php">창호견적</a></li>
<li class="swiper-slide"><a href="/sub02_1.php">시공견적</a></li>
<li class="swiper-slide"><a href="/sub02_2.php">창호소비효율등급제</a></li>
<li class="swiper-slide active "><a href="/sub02_3.php">창호리모델링시대</a></li>
<li class="swiper-slide "><a href="/sub02_4.php">고객이 알아야 할 창호시공 10선</a></li>
</ul>
</div>
</div>
</div>
<div id="container">
<div id="contArea">
<div class="sub-title">
<h2>창호리모델링시대</h2>
</div>
<div class="real-cont">
<!--// content -->
<div class="greetings">
<style>
/*** 이실장님 제공소스 ***/
@media screen and (max-width:992px){
.row {
display: flex;
flex-wrap: wrap;
margin: 0 auto;
width: 100%;
flex-direction: column;}
}
</style>
<div class="content">
<h3 class="doc-tit"><font color="#003300">집의 가치를 높이는 창호 리모델링, 왜 필요할까요?</font></h3>
<div class="row">
<div class="col-lg-5"><center><img class="img-responsive" src="/theme/rd.lwd/img/sub/sub/dn04.jpg" alt=""/></center></div>
<div class="col-lg-7">
<p>최근 몇 년 사이, <strong>창호 리모델링 시장이 빠르게 성장</strong>하고 있습니다.<br>
특히 코로나19 이후, 집에 머무는 시간이 늘어나면서 많은 가정에서 창호 교체를 고민하게 되었습니다.<br>
이전에는 바쁜 일상 때문에 미뤄왔던 창호 교체가, 이제는 실내 환경을 개선하는 필수적인 선택이 되고 있습니다.<br>
그렇다면, 창호 리모델링이 왜 중요한 걸까요?</p>
</div>
</div>
<div style="height: 1px; background-color: #eee; margin-bottom: 30px; margin-top: 10px;"></div>
<h3 class="doc-tit"><font color="#003300">단열과 에너지 절약, 생활비 절감의 시작</font></h3>
<div class="row">
<div class="col-lg-7">
<p>창호는 집 안의 온도를 유지하는 중요한 요소입니다. 오래된 창문은 틈새 바람이 많아 <strong>겨울엔 난방비가 오르고, 여름엔 에어컨 효율이 떨어지는</strong> 문제가 발생합니다.<br>하지만 최신 창호는 <strong>단열 성능이 뛰어나 에너지 절약 효과</strong>가 크며, 장기적으로 보면 난방비와 냉방비를 절약할 수 있습니다.<br>
특히 정부에서는 <strong>창호 소비효율 등급제</strong>를 도입해, 에너지 성능이 뛰어난 창호를 선택할 수 있도록 하고 있습니다. 또한 <strong>그린리모델링 지원 사업</strong>을 통해 에너지 절약형 창호로 교체하는 가정에 <strong>정부 지원금 혜택</strong>도 제공하고 있습니다.<br>이제 창호 교체는 단순한 인테리어가 아니라 <strong>경제적이고 환경을 생각하는 필수 투자</strong>가 되고 있는 것입니다.</p>
</div>
<div class="col-lg-5"><center><img class="img-responsive" src="/theme/rd.lwd/img/sub/sub/dn05.jpg" alt=""/></center></div>
</div>
<div style="height: 1px; background-color: #eee; margin-bottom: 30px; margin-top: 10px;"></div>
<h3 class="doc-tit"><font color="#003300">신축보다 ‘이동 시대’, 이사 가도 바뀌는 주거 환경</font></h3>
<div class="row">
<div class="col-lg-4"><center><img class="img-responsive" src="/theme/rd.lwd/img/sub/sub/dn06.jpg" alt=""/></center></div>
<div class="col-lg-8">
<p>과거에는 새 아파트를 짓고 입주하는 것이 트렌드였지만, <strong>인구 감소로 인해 신축 주택 공급이 줄어</strong>들고 있습니다.<br>
이제는 집에서 집으로 이동하며 주거 환경을 개선하는 시대가 왔습니다. 따라서 기존 주택을 보다 쾌적하게 바꾸기 위해 창호 <strong>리모델링을 통해 집의 가치를 높이는 것</strong>이 더욱 중요해졌습니다.<br>특히 <strong>방음과 결로 문제</strong>가 있는 오래된 창호는 쾌적한 생활을 방해합니다. 신혼부부, 어린아이를 키우는 가정, 재택근무를 하는 가정에서는 조용하고 쾌적한 환경이 필수적입니다.<br>창호 리모델링을 하면 <strong>소음 차단 효과</strong>가 뛰어나고, 실내 공기 질을 개선하여 가족의 건강까지 지킬 수 있습니다.<br><strong>창호 리모델링, 더 늦추지 마세요!</strong><br><br>
창호는 단순한 ‘창문’이 아닙니다.<br><strong>집 안 공기의 질, 온도 유지, 소음 차단, 에너지 절감</strong>까지 모든 요소에 영향을 미치는 중요한 부분입니다.<br>
이제는 단순한 미관 개선이 아니라 <strong>실용적인 이유로 창호 교체가 필수가 된 시대</strong>입니다.<br>
정부의 지원이 이루어지고 있는 지금, 창호 리모델링을 고려해보는 것은 어떨까요?<br>
우리 가족이 더 건강하고 쾌적한 공간에서 생활할 수 있도록, 현명한 선택을 해보세요!</p>
</div>
</div>
</div>
</div>
<!-- content //-->
</div>
</div>
</div>
<?php
include_once(G5_PATH.'/tail.php');
?>
@@ -0,0 +1,114 @@
<?php
include_once './_common.php';
include_once G5_LIB_PATH.'/thumbnail.lib.php';
include_once G5_PATH.'/head.php';
?>
<!-- for mobile -->
<div id="menu" class="mobile-navigation">
<div class="home"><a href="/"><img src="/theme/rd.lwd/img/sub/common/logo.png" alt="하이창호"></a></div>
<nav class="nav-menu"></nav>
<a href="javascript:void(0);" class="close">닫기</a>
</div>
<div class="mobile-overlay"></div>
<div class="sub-visual bg2">
<div class="inner">
<h2>Hi 고객스토리</h2>
</div>
</div>
<div class="lnb-wrap">
<div class="lnb">
<div class="swiper-container">
<ul class="swiper-wrapper">
<li class="swiper-slide "><a href="/sub02.php">창호견적</a></li>
<li class="swiper-slide"><a href="/sub02_1.php">시공견적</a></li>
<li class="swiper-slide"><a href="/sub02_2.php">창호소비효율등급제</a></li>
<li class="swiper-slide"><a href="/sub02_3.php">창호리모델링시대</a></li>
<li class="swiper-slide active "><a href="/sub02_4.php">고객이 알아야 할 창호시공 10선</a></li>
</ul>
</div>
</div>
</div>
<div id="container">
<div id="contArea">
<div class="sub-title">
<h2>고객이 알아야 할 창호시공 10선</h2>
</div>
<div class="real-cont">
<!--// content -->
<div class="greetings">
<style>
/*** 이실장님 제공소스 ***/
@media screen and (max-width:992px){
.row {
display: flex;
flex-wrap: wrap;
margin: 0 auto;
width: 100%;
flex-direction: column;}
}
</style>
<div class="content">
<h3 class="doc-tit"><font color="#003300">시공을 계획하고 계신가요? 아래는 창호 시공 시 꼭 알아두셔야 할 10가지 사항입니다.</font></h3>
<div class="row">
<!-- <div class="col-lg-3"><center><img class="img-responsive" src="/images/sub/dn04.jpg" alt=""/></center></div> -->
<div class="col-lg-12">
<p><strong><font color="#000000">1. 수직&middot;수평 정확도 확인</font></strong><br>
창호 시공 시 벽과 바닥의 수직•수평이 정확해야 합니다.<br>
작은 오차라도 누적되면 창문이 제대로 닫히지 않거나 틈이 생길 수 있습니다. 시공 전과 후에 수직&middot;수평 정밀도를 반드시 확인하세요.<br><br>
<strong><font color="#000000">2. 단열의 중요성</font></strong><br>
창호는 실내 온도 유지와 에너지 절약에 중요한 역할을 합니다.<br>
이중&middot;삼중 유리, 로이(Low-E) 코팅, 우레탄 폼 충진 등 단열 성능이 높은 제품과 시공 방식을 선택해야 냉&middot;난방비 절감 효과를 볼 수 있습니다.<br><br>
<strong><font color="#000000">3. 실리콘 마감으로 누수 방지</font></strong><br>
창문과 벽 사이의 실리콘 마감이 제대로 되어 있지 않으면 누수나 외풍이 발생할 수 있습니다.<br>
실리콘이 균일하게 발려 있는지, 틈이 없는지 꼼꼼히 확인하세요.<br><br>
<strong><font color="#000000">4. 단열 유리 선택</font></strong><br>
창문에 단열 유리를 사용하면 외부 온도 변화로부터 실내를 보호할 수 있습니다.<br>
특히 겨울철 결로 방지와 냉•난방비 절감 효과가 뛰어나므로, 창문 선택 시 단열 유리 여부를 체크하세요.<br><br>
<strong><font color="#000000">5. 슬라이딩 도어 및 창문 개폐 성능 확인</font></strong><br>
슬라이딩 도어나 여닫이 창이 부드럽게 열리고 닫히는지 확인해야 합니다.<br>
창틀과 레일이 정밀하게 맞아야 원활한 작동이 가능하며, 불량한 제품은 사용 시 불편함을 초래할 수 있습니다.<br><br>
<strong><font color="#000000">6. 잠금장치 및 하드웨어 품질 점검</font></strong><br>
창문의 잠금장치, 핸들, 경첩 등 하드웨어 품질이 낮으면 안전 문제나 사용 불편이 발생할 수 있습니다.<br>
특히 창문이 자주 여닫히는 공간에서는 내구성이 높은 하드웨어를 사용하는 것이 중요합니다.<br><br>
<strong><font color="#000000">7. 기밀성과 방음 성능 체크</font></strong><br>
창문이 제대로 밀폐되지 않으면 외풍이 들어오고 소음이 차단되지 않습니다.<br>
기밀 성능이 높은 제품을 선택하고, 창틀과 유리 접합 부위의 마감 상태를 점검하세요.<br><br>
<strong><font color="#000000">8. 폐기물 처리 방법 확인</font></strong><br>
기존 창호 철거 시 발생하는 폐기물 처리 비용과 방법을 미리 확인해야 합니다.<br>
일부 업체는 별도의 폐기물 처리 비용을 청구할 수 있으므로 계약 전에 포함 여부를 체크하세요.<br><br>
<strong><font color="#000000">9. A/S 기간 및 보증 조건 체크</font></strong><br>
창호 시공 후 문제가 발생할 수 있으므로, 제품과 시공의 A/S 기간과 보증 조건을 꼼꼼히 확인해야 합니다.<br>
창틀, 유리, 하드웨어 등에 대한 보증 기간이 각각 다를 수 있으니 사전에 계약서를 확인하세요.<br><br>
<strong><font color="#000000">10. 창문 몰딩 설치 여부</font></strong><br>
창문과 벽체 사이의 마감을 깔끔하게 처리하려면 몰딩을 설치할 수 있습니다.<br>
몰딩은 단열 및 방음 성능 향상에도 도움이 되며, 인테리어적으로도 완성도를 높일 수 있습니다. 하지만 추가 비용이 발생할 수 있으므로, 설치 여부를 미리 결정하는 것이 좋습니다.</p>
</div>
</div>
</div>
</div>
<!-- content //-->
</div>
</div>
</div>
<?php
include_once(G5_PATH.'/tail.php');
?>
+115
View File
@@ -0,0 +1,115 @@
<?php
include_once './_common.php';
include_once G5_LIB_PATH.'/thumbnail.lib.php';
include_once G5_PATH.'/head.php';
?>
<!-- for mobile -->
<div id="menu" class="mobile-navigation">
<div class="home"><a href="/"><img src="/theme/rd.lwd/img/sub/common/logo.png" alt="하이창호"></a></div>
<nav class="nav-menu"></nav>
<a href="javascript:void(0);" class="close">닫기</a>
</div>
<div class="mobile-overlay"></div>
<div class="sub-visual bg3">
<div class="inner">
<h2>창호 STORY</h2>
</div>
</div>
<div class="lnb-wrap">
<div class="lnb">
<div class="swiper-container">
<ul class="swiper-wrapper">
<li class="swiper-slide active "><a href="/sub03.php">창호제작 STORY</a></li>
<li class="swiper-slide "><a href="/sub03_2.php">시공 STORY</a></li>
<li class="swiper-slide "><a href="/sub03_3.php">유리 STORY</a></li>
<li class="swiper-slide "><a href="/sub03_4.php">창호소재&middot;종류 STORY</a></li>
<li class="swiper-slide"><a href="/sub03_5.php">창호브랜드</a></li>
</ul>
</div>
</div>
</div>
<div id="container">
<div id="contArea">
<div class="sub-title">
<h2>시공 STORY</h2>
</div>
<div class="real-cont">
<!--// content -->
<div class="greetings">
<style>
/*** 이실장님 제공소스 ***/
@media screen and (max-width:992px){
.row {
display: flex;
flex-wrap: wrap;
margin: 0 auto;
width: 100%;
flex-direction: column;}
}
</style>
<div class="content">
<!-- <h3 class="doc-tit"><font color="#003300">시공을 계획하고 계신가요? 아래는 창호 시공 시 꼭 알아두셔야 할 10가지 사항입니다.</font></h3> -->
<div class="row">
<!-- <div class="col-lg-3"><center><img class="img-responsive" src="/images/sub/dn04.jpg" alt=""/></center></div> -->
<div class="col-lg-6">
<p><strong><font color="#000000">1. 원자재 선택</font></strong><br>
창호의 틀이 되는 알루미늄 또는 PVC 프로파일을 선택해요.<br>
(PVC는 단열이 좋고, 알루미늄은 내구성이 뛰어나요!)
</div>
<div class="col-lg-6">
<strong><font color="#000000">2. 창호 절단</font></strong><br>
우리 집 창호 크기에 맞춰 프로파일을 정밀하게 절단해요.
</div>
<div class="col-lg-6">
<strong><font color="#000000">3. 보강재 삽입</font></strong><br>
PVC 창호 안에 강철 보강재를 넣어 튼튼하게 만들어요.
</div>
<div class="col-lg-6">
<strong><font color="#000000">4. 피스 체결</font></strong><br>
각 부품을 **나사(피스)**로 조립해 창틀을 완성해요.
</div>
<div class="col-lg-6">
<strong><font color="#000000">5. 모헤어 체결</font></strong><br>
창문 틈새에서 **바람이 새는 걸 막아주는 털(모헤어)**을 부착해요.
</div>
<div class="col-lg-6">
<strong><font color="#000000">6. 하드웨어 부착</font></strong><br>
손잡이, 경첩, 잠금장치 같은 **부자재(하드웨어)**를 설치해요.
</div>
<div class="col-lg-6">
<strong><font color="#000000">7. 창호 용접</font></strong><br>
창틀 모서리를 고온으로 용접하여 튼튼하게 고정해요.
</div>
<div class="col-lg-6">
<strong><font color="#000000">8. 방충망 제작</font></strong><br>
여름철 필수템! 벌레가 들어오지 않도록 방충망을 만들어 부착해요.
</div>
<div class="col-lg-6">
<strong><font color="#000000">9. 창호 유리 삽입</font></strong><br>
단열, 방음 효과를 높이는 유리를 창틀에 끼워 넣어요. (복층유리 사용 가능!)<br><br>
</div>
<div class="col-lg-6">
<strong><font color="#000000">10. 완성창 완성!</font></strong><br>
마지막 점검 후, 우리 집에 설치될 튼튼하고 아름다운 창호 완성!</p>
</div>
</div>
</div>
</div>
<!-- content //-->
</div>
</div>
</div>
<?php
include_once(G5_PATH.'/tail.php');
?>
@@ -0,0 +1,151 @@
<?php
include_once './_common.php';
include_once G5_LIB_PATH.'/thumbnail.lib.php';
include_once G5_PATH.'/head.php';
?>
<!-- for mobile -->
<div id="menu" class="mobile-navigation">
<div class="home"><a href="/"><img src="/theme/rd.lwd/img/sub/common/logo.png" alt="하이창호"></a></div>
<nav class="nav-menu"></nav>
<a href="javascript:void(0);" class="close">닫기</a>
</div>
<div class="mobile-overlay"></div>
<div class="sub-visual bg3">
<div class="inner">
<h2>창호 STORY</h2>
</div>
</div>
<div class="lnb-wrap">
<div class="lnb">
<div class="swiper-container">
<ul class="swiper-wrapper">
<li class="swiper-slide "><a href="/sub03.php">창호제작 STORY</a></li>
<li class="swiper-slide active "><a href="/sub03_2.php">시공 STORY</a></li>
<li class="swiper-slide "><a href="/sub03_3.php">유리 STORY</a></li>
<li class="swiper-slide "><a href="/sub03_4.php">창호소재&middot;종류 STORY</a></li>
<li class="swiper-slide"><a href="/sub03_5.php">창호브랜드</a></li>
</ul>
</div>
</div>
</div>
<div id="container">
<div id="contArea">
<div class="sub-title">
<h2>시공 STORY</h2>
</div>
<div class="real-cont">
<!--// content -->
<div class="greetings">
<style>
/*** 이실장님 제공소스 ***/
@media screen and (max-width:992px){
.row {
display: flex;
flex-wrap: wrap;
margin: 0 auto;
width: 100%;
flex-direction: column;}
}
</style>
<div class="content">
<h3 class="doc-tit"><font color="#3366cc">창문을 교체하려는데, 어떻게 진행되는지 궁금하시죠? 걱정 마세요! 하나하나 차근차근 설명해 드릴게요.</font></h3>
<div class="row">
<!-- <div class="col-lg-3"><center><img class="img-responsive" src="/images/sub/dn04.jpg" alt=""/></center></div> -->
<div class="col-lg-12">
<p><strong><font color="#cc0000">1단계: 보호 작업 (보양) – 우리 집을 깨끗하게 지키는 과정!</font></strong><br>
<i class="xi-help"></i><strong>주부님</strong> : 시공할 때 집이 많이 더러워지거나 가구가 망가질까 걱정돼요. 어떻게 하나요?<br>
<i class="xi-profile"></i><strong>시공자</strong> : 네, 그런 걱정 많으시죠! 창문 공사를 할 때 먼지가 날리고 가구가 긁힐 수도 있어서, 미리 보호 작업을 해요.</p>
<p><strong><font color="#000000">우리가 하는 일</font></strong><br>
<i class="xi-toggle-on"></i> 깨지기 쉬운 물건(화분, 액자, 도자기 등)은 안전한 곳으로 옮겨요.<br>
<i class="xi-toggle-on"></i> 소파, 식탁, 책상 같은 큰 가구는 작업 공간에서 잠시 이동해 둬요.<br>
<i class="xi-toggle-on"></i> 먼지가 날릴 수 있으니 바닥에 보호 매트를 깔아요.<br>
<i class="xi-toggle-on"></i> 공사가 끝나면 가구를 원래 자리로 돌려놓고 깨끗하게 정리해 드려요.</p>
<p><strong><font color="#000000">주부님 TIP!</font></strong><br>
<i class="xi-key"></i> 보양 작업을 미리 해 두면 청소하는 시간이 줄어들어요!</p>
<div style="height: 1px; background-color: #eee;"></div>
</div>
<div class="col-lg-12">
<p><strong><font color="#cc0000">2단계: 기존 창문 철거 – 낡은 창문을 조심스럽게 제거하는 과정!</font></strong><br>
<i class="xi-help"></i><strong>주부님</strong> : 기존 창문을 떼어낼 때 벽이 망가지진 않을까요?<br>
<i class="xi-profile"></i><strong>시공자</strong> : 좋은 질문이에요! 창문을 급하게 철거하면 벽지나 타일이 손상될 수 있어서, 최대한 조심스럽게 작업해요.</p>
<p><strong><font color="#000000">우리가 신경 쓰는 부분</font></strong><br>
<i class="xi-toggle-on"></i> 창문을 떼어낼 때 벽지, 타일, 바닥이 다치지 않도록 신중하게 철거해요.<br>
<i class="xi-toggle-on"></i> 철거 후 창문 주변을 살펴보고, 손상된 부분이 없는지 확인해요.</p>
<p><strong><font color="#000000">주부님 TIP!</font></strong><br>
<i class="xi-key"></i> 기존 창문을 철거하면서 벽이나 타일이 손상되었는지 한 번 더 체크해 주세요!</p>
<div style="height: 1px; background-color: #eee;"></div>
</div>
<div class="col-lg-12">
<p><strong><font color="#cc0000">3단계: 새 창문 옮기기(양중) – 새로운 창문을 안전하게 들여오는 과정!</font></strong><br>
<i class="xi-help"></i><strong>주부님</strong> : 새 창문이 도착하면 어떻게 운반하나요?<br>
<i class="xi-profile"></i><strong>시공자</strong> : 창문은 크고 무겁기 때문에 안전하게 옮기는 게 제일 중요해요.</p>
<p><strong><font color="#000000">우리가 신경 쓰는 부분</font></strong><br>
<i class="xi-toggle-on"></i> 창문을 조심스럽게 옮겨서 벽이나 바닥이 손상되지 않도록 해요.<br>
<i class="xi-toggle-on"></i> 아이들이 다칠 위험이 있으니, 주변에 안전거리를 확보해요.</p>
<p><strong><font color="#000000">주부님 TIP!</font></strong><br>
<i class="xi-key"></i> 창문 운반 중 아이들이 주변에서 뛰어놀지 않도록 주의해 주세요!</p>
<div style="height: 1px; background-color: #eee;"></div>
</div>
<div class="col-lg-12">
<p><strong><font color="#cc0000">4단계: 새 창문 설치(시공) – 창문을 튼튼하게 고정하는 과정!</font></strong><br>
<i class="xi-help"></i><strong>주부님</strong> : 창문이 바람이 새거나 삐뚤어지진 않을까요?<br>
<i class="xi-profile"></i><strong>시공자</strong> : 그런 걱정하지 않으셔도 돼요! 창문이 완벽하게 고정되도록 꼼꼼하게 작업해요.</p>
<p><strong><font color="#000000">우리가 하는 일</font></strong><br>
<i class="xi-toggle-on"></i> 창문이 기울어지지 않도록 수평을 맞춰서 설치해요.<br>
<i class="xi-toggle-on"></i> 창틀을 튼튼하게 고정해서 흔들리지 않도록 해요.</p>
<i class="xi-toggle-on"></i> 창문과 벽 사이의 틈을 막기 위해 우레탄폼과 실리콘을 꼼꼼하게 발라요.</p>
<p><strong><font color="#000000">주부님 TIP!</font></strong><br>
<i class="xi-key"></i> 창문 설치 후 열고 닫아보면서 이상이 없는지 직접 확인해 보세요!</p>
<div style="height: 1px; background-color: #eee;"></div>
</div>
<div class="col-lg-12">
<p><strong><font color="#cc0000">5단계: 마무리 정리 – 깨끗하게 마무리하고 사용법을 알려드리는 과정!</font></strong><br>
<i class="xi-help"></i><strong>주부님</strong> : 시공이 끝난 후, 어떤 걸 확인해야 하나요?<br>
<i class="xi-profile"></i><strong>시공자</strong> : 공사가 끝나면 마무리 작업을 하고, 주부님께 사용 방법과 주의 사항을 설명해 드려요.</p>
<p><strong><font color="#000000">우리가 하는 일</font></strong><br>
<i class="xi-toggle-on"></i> 창문 손잡이를 설치하고, 정상적으로 열리고 닫히는지 확인해요.<br>
<i class="xi-toggle-on"></i> 보호 매트와 보양재를 치우고, 주변을 깨끗하게 정리해요.</p>
<i class="xi-toggle-on"></i> 창문 사용 방법과 A/S 관련 사항을 안내해 드려요.</p>
<p><strong><font color="#000000">주부님 TIP!</font></strong><br>
<i class="xi-key"></i> 창문이 잘 닫히는지, 잠금장치가 제대로 작동하는지 직접 테스트해 보세요!</p>
<div style="height: 1px; background-color: #eee;"></div>
</div>
<div class="col-lg-12">
<p><strong><font color="#cc0000">한눈에 보는 창문 교체 과정!</font></strong><br>
1. 보호 작업 – 가구와 물건을 정리해서 깨끗한 작업 환경을 만든다!<br>
2. 철거 작업 – 오래된 창문을 조심스럽게 제거한다!<br>
3. 새 창문 옮기기 – 안전하게 새 창문을 들여온다!<br>
4. 새 창문 설치 – 창문이 흔들리지 않도록 튼튼하게 고정한다!<br>
5. 마무리 정리 – 깨끗하게 정리하고 창문 사용법을 배운다!</p>
</div>
</div>
</div>
</div>
<!-- content //-->
</div>
</div>
</div>
<?php
include_once(G5_PATH.'/tail.php');
?>
@@ -0,0 +1,186 @@
<?php
include_once './_common.php';
include_once G5_LIB_PATH.'/thumbnail.lib.php';
include_once G5_PATH.'/head.php';
?>
<!-- for mobile -->
<div id="menu" class="mobile-navigation">
<div class="home"><a href="/"><img src="/theme/rd.lwd/img/sub/common/logo.png" alt="하이창호"></a></div>
<nav class="nav-menu"></nav>
<a href="javascript:void(0);" class="close">닫기</a>
</div>
<div class="mobile-overlay"></div>
<div class="sub-visual bg3">
<div class="inner">
<h2>창호 STORY</h2>
</div>
</div>
<div class="lnb-wrap">
<div class="lnb">
<div class="swiper-container">
<ul class="swiper-wrapper">
<li class="swiper-slide "><a href="/sub03.php">창호제작 STORY</a></li>
<li class="swiper-slide "><a href="/sub03_2.php">시공 STORY</a></li>
<li class="swiper-slide active "><a href="/sub03_3.php">유리 STORY</a></li>
<li class="swiper-slide "><a href="/sub03_4.php">창호소재&middot;종류 STORY</a></li>
<li class="swiper-slide"><a href="/sub03_5.php">창호브랜드</a></li>
</ul>
</div>
</div>
</div>
<div id="container">
<div id="contArea">
<div class="sub-title">
<h2>유리 STORY</h2>
</div>
<div class="real-cont">
<!--// content -->
<div class="greetings">
<style>
/*** 이실장님 제공소스 ***/
@media screen and (max-width:992px){
.row {
display: flex;
flex-wrap: wrap;
margin: 0 auto;
width: 100%;
flex-direction: column;}
}
</style>
<div class="content">
<!-- <h3 class="doc-tit"><font color="#3366cc">창문을 교체하려는데, 어떻게 진행되는지 궁금하시죠? 걱정 마세요! 하나하나 차근차근 설명해 드릴게요.</font></h3> -->
<p>모든 유리는 기본적으로 판유리(Flat Glass)에서 출발합니다. 판유리는 플로트 공법을 통해 대량 생산되며, 이후 다양한 가공 과정을 거쳐 접합유리, 복층유리, 인테리어유리, 코팅유리 등의 형태로 발전됩니다.<br>창문에 사용되는 유리는 복층유리와 코팅유리입니다. 과거에는 코팅유리에 대한 수요가 적었지만 ‘창호소비효율등급제’의 도입으로 코팅유리 사용이 급격하게 늘어나고 있습니다.</p>
<div class="row">
<!-- <div class="col-lg-3"><center><img class="img-responsive" src="/images/sub/dn04.jpg" alt=""/></center></div> -->
<div class="col-lg-12">
<p><strong><font color="#cc0000">- 판유리 제작 과정(Flat Glass Manufacturing Process)</font></strong><br>
<strong>① 원료 혼합 (Batching)</strong><br>
● 주원료: 실리카 모래(SiO₂), 소다회(Na₂CO₃), 석회석(CaCO₃)<br>
● 첨가제: 철분 제거제(투명도 증가), 산화알루미늄(내구성 증가) 등<br>
● 철저한 비율로 혼합 후 용융로로 이동<br>
<strong>② 용융 (Melting)</strong><br>
● 1,500°C 이상의 고온에서 **용융로(Furnace)**에서 가열하여 유리 원료를 녹임<br>
● 불순물을 제거하고 균질한 유리 용융물 형성<br>
<strong>③ 플로트 공법 (Float Process)</strong><br>
● 녹은 유리를 주석(Tin) 용탕 위에 부어 자연스럽게 평평한 형태로 만듦<br>
● 주석 위에서 유리가 부유하면서 매끄럽고 균일한 표면 형성<br>
<strong>④ 냉각 및 어닐링 (Cooling & Annealing)</strong><br>
● **어닐링로(Annealing Lehr)**에서 서서히 냉각하여 내부 응력 제거<br>
● 강도와 내구성 확보<br>
<strong>⑤ 절단 및 가공 (Cutting & Processing)</strong><br>
● 원하는 크기와 두께로 절단 후, 필요에 따라 연마, 강화, 코팅 등 추가 가공 진행</p>
<div style="height: 1px; background-color: #eee;"></div>
</div>
<div class="col-lg-12">
<p><strong><font color="#cc0000">- 판유리에서 다양한 유리로 가공(Types of Processed Glass)</font></strong><br>
<strong><font color="#660000"><i class="xi-check-square"></i> 접합유리 (Laminated Glass)</font></strong><br>
<i class="xi-check-square-o"></i> <strong>특징 : 두 장 이상의 유리 사이에 PVB(Polyvinyl Butyral) 필름을 넣고 압착하여 만든 유리</strong><br>
<i class="xi-check-square-o"></i> <strong>제작 원리</strong><br>
1. 두 장 이상의 판유리 사이에 <strong>PVB 필름</strong> 삽입<br>
2. 고온과 압력을 가해 유리와 필름을 밀착<br>
3. 충격을 받아도 파편이 필름에 붙어 있어 <strong>안전성 강화</strong><br>
<i class="xi-check-square-o"></i> <strong>사용처: 자동차 유리, 방탄 유리, 고층 건물 외벽</strong></p>
<p>&nbsp;</p>
<p><strong><font color="#660000"><i class="xi-check-square"></i> 복층유리(Insulated Glass, IGU)</font></strong><br>
<i class="xi-check-square-o"></i> <strong>특징 : 두 장 이상의 유리 사이에 **공기층(또는 가스층)**을 두어 단열 및 방음 효과를 높인 유리</strong><br>
<i class="xi-check-square-o"></i> <strong>제작 원리</strong><br>
1. 두 장 이상의 유리를 절단 및 세척<br>
2. 유리 사이에 간봉(Spacer) + 건조제(Desiccant) 삽입<br>
3. 내부 공간에 아르곤(Argon) 가스 또는 공기 주입<br>
4. 실란트(Sealant)로 완전 밀봉<br>
<i class="xi-check-square-o"></i> <strong>사용처: 건축 외장재, 에너지 절약 창호, 친환경 건물</strong></p>
<p>&nbsp;</p>
<p><strong><font color="#660000"><i class="xi-check-square"></i> 인테리어유리(Interior Glass)</font></strong><br>
<i class="xi-check-square-o"></i> <strong>특징 : 다양한 패턴, 색상, 질감을 적용해 미적 감각을 높인 유리</strong><br>
<i class="xi-check-square-o"></i> <strong>종류 및 제작 원리</strong><br>
● <strong>강화유리(Tempered Glass)</strong>: 급속 냉각하여 충격 저항을 높임 (샤워 부스, 테이블)<br>
● <strong>패턴유리(Patterned Glass)</strong>: 롤러 프레스 방식으로 표면에 무늬를 새김 (파티션, 문)<br>
● <strong>컬러유리(Tinted Glass)</strong>: 산화금속을 첨가하여 색상을 부여 (인테리어 장식)<br>
<i class="xi-check-square-o"></i> <strong>사용처: 실내 디자인, 가구, 파티션, 샤워부스</strong></p>
<p>&nbsp;</p>
<p><strong><font color="#660000"><i class="xi-check-square"></i> 코팅유리(Coated Glass)</font></strong><br>
<i class="xi-check-square-o"></i> <strong>특징 : 유리 표면에 특수 코팅을 하여 기능성을 강화한 유리</strong><br>
<i class="xi-check-square-o"></i> <strong>종류 및 제작 원리</strong><br>
● <strong>로이유리(Low-E Glass)</strong>: 적외선 반사 코팅 → 단열 성능 증가<br>
● <strong>반사유리(Reflective Glass)</strong>: 금속 산화물 코팅 → 태양열 차단<br>
● <strong>자가청정유리(Self-Cleaning Glass)</strong>: 나노 코팅 → 빗물로 먼지를 자동 세척<br>
<i class="xi-check-square-o"></i> <strong>사용처: 에너지 절약 창호, 고급 건물 외벽, 친환경 건축</strong></p>
<div style="height: 1px; background-color: #eee;"></div>
</div>
<div class="col-lg-12">
<p><strong><font color="#cc0000">- 유리 가공별 차이점</font></strong></p>
<!-- 표 -->
<div class="doc-cnt">
<div class="mana-table">
<table>
<thead>
<tr>
<th>유리 종류</th>
<th>특징</th>
<th>제작 원리</th>
<th>주요 용도</th>
</tr>
</thead>
<tbody>
<tr>
<td><center><strong>접합유리</strong></center></td>
<td><center>안전성 강화</center></td>
<td>PVB 필름 접합</td>
<td>자동차, 방탄 유리</td>
</tr>
<tr>
<td><center><strong>복층유리</strong></center></td>
<td><center>단열, 방음</center></td>
<td>유리 사이 공기층 형성</td>
<td>창호, 건축 외장재</td>
</tr>
<tr>
<td><center><strong>인테리어유리</strong></center></td>
<td><center>미적 감각</center></td>
<td>강화, 패턴, 컬러 적용</td>
<td>실내 디자인, 가구</td>
</tr>
<tr>
<td><center><strong>코팅유리</strong></center></td>
<td><center>기능성 강화</center></td>
<td>로이, 반사, 자가청정 코팅</td>
<td>에너지 절감, 친환경 건축</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- //표 -->
</div>
</div>
</div>
</div>
<!-- content //-->
</div>
</div>
</div>
<?php
include_once(G5_PATH.'/tail.php');
?>
@@ -0,0 +1,304 @@
<?php
include_once './_common.php';
include_once G5_LIB_PATH.'/thumbnail.lib.php';
include_once G5_PATH.'/head.php';
?>
<!-- for mobile -->
<div id="menu" class="mobile-navigation">
<div class="home"><a href="/"><img src="/theme/rd.lwd/img/sub/common/logo.png" alt="하이창호"></a></div>
<nav class="nav-menu"></nav>
<a href="javascript:void(0);" class="close">닫기</a>
</div>
<div class="mobile-overlay"></div>
<div class="sub-visual bg3">
<div class="inner">
<h2>창호 STORY</h2>
</div>
</div>
<div class="lnb-wrap">
<div class="lnb">
<div class="swiper-container">
<ul class="swiper-wrapper">
<li class="swiper-slide "><a href="/sub03.php">창호제작 STORY</a></li>
<li class="swiper-slide "><a href="/sub03_2.php">시공 STORY</a></li>
<li class="swiper-slide "><a href="/sub03_3.php">유리 STORY</a></li>
<li class="swiper-slide active "><a href="/sub03_4.php">창호소재&middot;종류 STORY</a></li>
<li class="swiper-slide"><a href="/sub03_5.php">창호브랜드</a></li>
</ul>
</div>
</div>
</div>
<div id="container">
<div id="contArea">
<div class="sub-title">
<h2>창호소재&middot;종류 STORY</h2>
</div>
<div class="real-cont">
<!--// content -->
<div class="greetings">
<style>
/*** 이실장님 제공소스 ***/
@media screen and (max-width:992px){
.row {
display: flex;
flex-wrap: wrap;
margin: 0 auto;
width: 100%;
flex-direction: column;}
}
</style>
<div class="content">
<!-- <h3 class="doc-tit"><font color="#3366cc">창문을 교체하려는데, 어떻게 진행되는지 궁금하시죠? 걱정 마세요! 하나하나 차근차근 설명해 드릴게요.</font></h3> -->
<p><strong><font color="#000000">창의 형태별 분류와 특성, 설치 장소</font></strong><br>
창호는 건물의 디자인과 기능성을 결정하는 중요한 요소로, 설치 목적과 구조에 따라 다양한 형태로 나뉩니다. 여기에서는 대표적인 창의 형태(단창, 이중창, 발코니창 등)를 설명하고, 각각의 <strong>특성</strong>과 <strong>설치 장소</strong>를 정리해보겠습니다.</p>
<div class="row">
<div class="col-lg-6">
<h3 class="doc-tit"><font color="#003300">단창(Single Window, 單窓)</font></h3>
<p><strong><font color="#000000"><i class="xi-check-square"></i> 유리 한 장(1겹)으로 된 창</font></strong><br>
<strong><font color="#000000"><i class="xi-check-square"></i> 특성</font></strong><br>
● 유리가 한 장만 사용되기 때문에 <strong>단열 성능이 낮음</strong><br>
● <strong>가격이 저렴하고 가벼움</strong><br>
● 방음, 기밀성, 단열 성능이 상대적으로 부족<br>
● 과거에는 많이 사용되었으나, 현재는 <strong>주택에서는 거의 사용되지 않음</strong><br>
<strong><font color="#000000"><i class="xi-check-square"></i> 설치 장소</font></strong><br>
● 창고, 실외 기계실, 임시 건물 등<br>
● 단열이 크게 중요하지 않은 공간</p>
<!-- <div style="height: 1px; background-color: #eee;"></div> -->
</div>
<div class="col-lg-6">
<h3 class="doc-tit"><font color="#003300">이중창(Double Window, 二重窓)</font></h3>
<p><strong><font color="#000000"><i class="xi-check-square"></i> 두 개의 창이 앞뒤로 이중 구조를 이루는 창</font></strong><br>
<strong><font color="#000000"><i class="xi-check-square"></i> 특성</font></strong><br>
● 창이 두 겹으로 되어 있어 <strong>단열&middot;방음 성능이 우수</strong><br>
● 외창(AL창)과 내창(PVC창) 조합으로 제작 가능<br>
● <strong>겨울철 결로 방지 효과가 뛰어남</strong><br>
● 개폐 방식에 따라 <strong>미서기창, 여닫이창, 고정창</strong> 등 다양한 형태로 제작<br>
<strong><font color="#000000"><i class="xi-check-square"></i> 설치 장소</font></strong><br>
● 단열이 중요한 <strong>거실, 침실, 발코니 등 주거 공간</strong><br>
● 고층 아파트, 빌라 등 <strong>외부 소음이 심한 곳</strong><br>
● 난방비 절감을 원하는 공간</p>
<!-- <div style="height: 1px; background-color: #eee;"></div> -->
</div>
<div class="col-lg-6">
<h3 class="doc-tit"><font color="#003300">발코니창(Balcony Window, 陽臺窓)</font></h3>
<p><strong><font color="#000000"><i class="xi-check-square"></i> 발코니와 실내 공간을 구분하는 창</font></strong><br>
<strong><font color="#000000"><i class="xi-check-square"></i> 특성</font></strong><br>
● 일반적으로 <strong>이중창 구조</strong>를 사용하여 단열•방음 성능 강화<br>
● 단열 및 방음 효과가 좋아야 하며, 내•외부 기밀성이 중요<br>
● 개폐 방식은 <strong>미서기형(슬라이딩형)이 가장 일반적</strong><br>
● 일부 아파트에서는 발코니 확장 공사로 인해 제거되기도 함<br>
<strong><font color="#000000"><i class="xi-check-square"></i> 설치 장소</font></strong><br>
● 아파트, 빌라 발코니 공간<br>
● 외부와 연결된 테라스, 베란다</p>
<!-- <div style="height: 1px; background-color: #eee;"></div> -->
</div>
<div class="col-lg-6">
<h3 class="doc-tit"><font color="#003300">고정창(Fixed Window, 固定窓)</font></h3>
<p><strong><font color="#000000"><i class="xi-check-square"></i> 열리지 않는 창 (개폐 불가능)</font></strong><br>
<strong><font color="#000000"><i class="xi-check-square"></i> 특성</font></strong><br>
● 개방 기능이 없는 <strong>유리만 있는 구조</strong><br>
● 주로 <strong>채광을 위한 용도</strong>로 사용<br>
● 단열 및 방음 성능이 높음 (틈이 없음)<br>
● 디자인 요소로 활용되며, 다른 창과 함께 조합하는 경우 많음<br>
<strong><font color="#000000"><i class="xi-check-square"></i> 설치 장소</font></strong><br>
● 상업 건물의 외관 창 (고층 빌딩 등)<br>
● 조망이 중요한 거실, 호텔, 카페<br>
● 계단실, 복도 등 채광이 필요한 공간</p>
<!-- <div style="height: 1px; background-color: #eee;"></div> -->
</div>
<div class="col-lg-6">
<h3 class="doc-tit"><font color="#003300">미서기창(Sliding Window, 片引窓)</font></h3>
<p><strong><font color="#000000"><i class="xi-check-square"></i> 좌우로 미는 방식(슬라이딩)으로 여닫는 창</font></strong><br>
<strong><font color="#000000"><i class="xi-check-square"></i> 특성</font></strong><br>
● 가장 보편적으로 사용되는 개폐 방식<br>
● 공간 활용도가 높아 좁은 공간에서도 설치 가능<br>
● 단열, 기밀성은 여닫이창보다 낮음 (틈이 생길 가능성이 있음)<br>
<strong><font color="#000000"><i class="xi-check-square"></i> 설치 장소</font></strong><br>
● 거실, 침실, 발코니, 주방<br>
● 좁은 공간이 많은 소형 아파트, 빌라</p>
<!-- <div style="height: 1px; background-color: #eee;"></div> -->
</div>
<div class="col-lg-6">
<h3 class="doc-tit"><font color="#003300">여닫이창(Casement Window, 開閉窓)</font></h3>
<p><strong><font color="#000000"><i class="xi-check-square"></i> 안쪽 또는 바깥쪽으로 열리는 창</font></strong><br>
<strong><font color="#000000"><i class="xi-check-square"></i> 특성</font></strong><br>
● <strong>기밀성과 단열 성능이 뛰어남</strong>(틈이 없음)<br>
● 완전히 개방할 수 있어 <strong>환기 효과가 좋음</strong><br>
● 바람이 강한 날에는 흔들리거나 파손될 위험이 있음<br>
<strong><font color="#000000"><i class="xi-check-square"></i> 설치 장소</font></strong><br>
● 단독주택, 고급 주거공간<br>
● 전원주택, 별장, 호텔</p>
<!-- <div style="height: 1px; background-color: #eee;"></div> -->
</div>
<div class="col-lg-6">
<h3 class="doc-tit"><font color="#003300">폴딩도어(Folding Door, 折りたたみ窓)</font></h3>
<p><strong><font color="#000000"><i class="xi-check-square"></i> 접이식으로 여러 장의 창을 접었다 펴는 구조</font></strong><br>
<strong><font color="#000000"><i class="xi-check-square"></i> 특성</font></strong><br>
● 넓은 개방감을 제공하며 <strong>실내외 연결성이 뛰어남</strong><br>
● 일반 창보다 <strong>비용이 높고 설치 난이도가 있음</strong><br>
● 공간 활용성이 좋아 카페나 고급 주택에서 많이 사용<br>
<strong><font color="#000000"><i class="xi-check-square"></i> 설치 장소</font></strong><br>
● 테라스, 실내외 연결 공간 (거실 ↔ 마당)<br>
● 카페, 레스토랑, 상업용 공간</p>
<!-- <div style="height: 1px; background-color: #eee;"></div> -->
</div>
<div class="col-lg-6">
<h3 class="doc-tit"><font color="#003300">망입유리창(Wire Glass Window, 網入玻璃窓)</font></h3>
<p><strong><font color="#000000"><i class="xi-check-square"></i> 유리 내부에 철망이 삽입된 창</font></strong><br>
<strong><font color="#000000"><i class="xi-check-square"></i> 특성</font></strong><br>
● 유리가 깨져도 <strong>파편이 날리지 않도록 설계</strong><br>
● 화재 발생 시 <strong>화염 확산을 방지하는 역할</strong><br>
● 방범&middot;안전성을 고려한 창<br>
<strong><font color="#000000"><i class="xi-check-square"></i> 설치 장소</font></strong><br>
● 공장, 창고, 학교, 화재 위험이 높은 건물<br>
● 방범이 필요한 상업시설</p>
<!-- <div style="height: 1px; background-color: #eee;"></div> -->
</div>
<div class="col-lg-6">
<h3 class="doc-tit"><font color="#003300">루버창(Louver Window, 羽板窓)</font></h3>
<p><strong><font color="#000000"><i class="xi-check-square"></i> 여러 개의 가로 날개(루버)를 조절하여 개폐하는 창</font></strong><br>
<strong><font color="#000000"><i class="xi-check-square"></i> 특성</font></strong><br>
● 개방 각도를 조절하여 <strong>바람 조절 및 환기가 용이</strong><br>
● 외부에서 내부가 잘 보이지 않아 <strong>프라이버시 보호 효과</strong><br>
● 방수 및 기밀성이 낮아 비바람이 많은 지역에서는 사용 제한<br>
<strong><font color="#000000"><i class="xi-check-square"></i> 설치 장소</font></strong><br>
● 화장실, 욕실, 보일러실, 기계실<br>
● 환기가 필요한 공간</p>
<!-- <div style="height: 1px; background-color: #eee;"></div> -->
</div>
<div class="col-lg-12">
<p><strong><font color="#cc0000">- 창 형태별 주요 비교</font></strong></p>
<!-- 표 -->
<div class="doc-cnt">
<div class="mana-table">
<table>
<thead>
<tr>
<th>창 종류</th>
<th>개폐 방식</th>
<th>단열성</th>
<th>방음성</th>
<th>특징</th>
<th>주로 쓰이는 장소</th>
</tr>
</thead>
<tbody>
<tr>
<td><center><strong>단창</strong></center></td>
<td>단일 유리</td>
<td><center>낮음</center></td>
<td><center>낮음</center></td>
<td>저렴하지만 단열&middot;방음 약함</td>
<td>창고, 실외 공간</td>
</tr>
<tr>
<td><center><strong>이중창</strong></center></td>
<td>슬라이딩 or 여닫이</td>
<td><center>높음</center></td>
<td><center>높음</center></td>
<td>외창+내창 구조, 단열성 우수</td>
<td>주거공간, 거실, 침실</td>
</tr>
<tr>
<td><center><strong>발코니창</strong></center></td>
<td>슬라이딩</td>
<td><center>높음</center></td>
<td><center>높음</center></td>
<td>발코니 확장 시 중요</td>
<td>아파트, 빌라 발코니</td>
</tr>
<tr>
<td><center><strong>고정창</strong></center></td>
<td>없음</td>
<td><center>높음</center></td>
<td><center>높음</center></td>
<td>개폐 불가능, 조망&middot;채광용</td>
<td>빌딩, 카페, 거실</td>
</tr>
<tr>
<td><center><strong>미서기창</strong></center></td>
<td>좌우 슬라이딩</td>
<td><center>보통</center></td>
<td><center>보통</center></td>
<td>공간 활용도 높음</td>
<td>거실, 주방, 발코니</td>
</tr>
<tr>
<td><center><strong>여닫이창</strong></center></td>
<td>안팎 개폐</td>
<td><center>높음</center></td>
<td><center>높음</center></td>
<td>기밀성 우수, 환기 효과 큼</td>
<td>전원주택, 고급 주택</td>
</tr>
<tr>
<td><center><strong>폴딩도어</strong></center></td>
<td>접이식</td>
<td><center>보통</center></td>
<td><center>보통</center></td>
<td>넓은 개방감</td>
<td>테라스, 카페</td>
</tr>
<tr>
<td><center><strong>망입유리창</strong></center></td>
<td>없음</td>
<td><center>보통</center></td>
<td><center>보통</center></td>
<td>화재&middot;방범 보호</td>
<td>공장, 창고</td>
</tr>
<tr>
<td><center><strong>루버창</strong></center></td>
<td>날개 조절</td>
<td><center>낮음</center></td>
<td><center>낮음</center></td>
<td>환기 용이, 방수 약함</td>
<td>욕실, 기계실</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- //표 -->
</div>
<div class="col-lg-12">
<h3 class="doc-tit"><font color="#003300">결론</font></h3>
<p>● 단열&middot;방음이 중요하다면? → <strong>이중창, 여닫이창</strong><br>
● 개방감이 필요하다면? → <strong>폴딩도어</strong><br>
● 환기가 중요하다면? → <strong>루버창</strong><br>
● 방범•안전이 필요하다면? → <strong>망입유리창</strong><br>
용도와 공간 특성을 고려하여 적절한 창을 선택하는 것이 중요합니다.</p>
</div>
</div>
</div>
</div>
<!-- content //-->
</div>
</div>
</div>
<?php
include_once(G5_PATH.'/tail.php');
?>
@@ -0,0 +1,75 @@
<?php
include_once './_common.php';
include_once G5_LIB_PATH.'/thumbnail.lib.php';
include_once G5_PATH.'/head.php';
?>
<!-- for mobile -->
<div id="menu" class="mobile-navigation">
<div class="home"><a href="/"><img src="/theme/rd.lwd/img/sub/common/logo.png" alt="하이창호"></a></div>
<nav class="nav-menu"></nav>
<a href="javascript:void(0);" class="close">닫기</a>
</div>
<div class="mobile-overlay"></div>
<div class="sub-visual bg3">
<div class="inner">
<h2>창호 STORY</h2>
</div>
</div>
<div class="lnb-wrap">
<div class="lnb">
<div class="swiper-container">
<ul class="swiper-wrapper">
<li class="swiper-slide "><a href="/sub03.php">창호제작 STORY</a></li>
<li class="swiper-slide "><a href="/sub03_2.php">시공 STORY</a></li>
<li class="swiper-slide "><a href="/sub03_3.php">유리 STORY</a></li>
<li class="swiper-slide "><a href="/sub03_4.php">창호소재&middot;종류 STORY</a></li>
<li class="swiper-slide active "><a href="/sub03_5.php">창호브랜드</a></li>
</ul>
</div>
</div>
</div>
<div id="container">
<div id="contArea">
<div class="sub-title">
<h2>창호브랜드</h2>
</div>
<div class="real-cont">
<!--// content -->
<div class="greetings">
<style>
/*** 이실장님 제공소스 ***/
@media screen and (max-width:992px){
.row {
display: flex;
flex-wrap: wrap;
margin: 0 auto;
width: 100%;
flex-direction: column;}
}
</style>
<div class="content">
<!-- <h3 class="doc-tit"><font color="#3366cc">창문을 교체하려는데, 어떻게 진행되는지 궁금하시죠? 걱정 마세요! 하나하나 차근차근 설명해 드릴게요.</font></h3> -->
<p><!-- <strong><font color="#000000">창의 형태별 분류와 특성, 설치 장소</font></strong><br> -->
<center>준비중입니다!</center></p>
</div>
</div>
<!-- content //-->
</div>
</div>
</div>
<?php
include_once(G5_PATH.'/tail.php');
?>
@@ -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">&times;</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>';
?>
@@ -0,0 +1,540 @@
/* 설문 목록 모듈 스타일 */
.survey-list-module {
--survey-primary: #AA20FF;
--survey-primary-light: rgba(170, 32, 255, 0.1);
--survey-primary-dark: #8A1ACC;
}
.survey-section {
padding: 80px 0;
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
}
.survey-section .section-header {
text-align: center;
margin-bottom: 60px;
}
.survey-section .section-header .subtitle {
color: var(--survey-primary);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 2px;
font-size: 0.9em;
margin-bottom: 10px;
display: block;
}
.survey-section .section-header h2 {
font-size: 2.5em;
font-weight: 700;
color: #333;
margin-bottom: 20px;
line-height: 1.2;
}
.survey-section .section-header p {
font-size: 1.1em;
color: #666;
max-width: 600px;
margin: 0 auto;
line-height: 1.6;
}
.survey-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
gap: 30px;
margin-bottom: 50px;
}
.survey-card {
background: white;
border-radius: 20px;
padding: 30px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
transition: all 0.4s ease;
position: relative;
overflow: hidden;
cursor: pointer;
border: 2px solid transparent;
}
.survey-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, var(--survey-primary) 0%, var(--survey-primary-dark) 100%);
transform: scaleX(0);
transition: transform 0.4s ease;
}
.survey-card:hover {
transform: translateY(-10px);
box-shadow: 0 20px 40px rgba(0,0,0,0.15);
border-color: var(--survey-primary-light);
}
.survey-card:hover::before {
transform: scaleX(1);
}
.survey-card-header {
margin-bottom: 20px;
}
.survey-title {
font-size: 1.4em;
font-weight: 600;
color: #333;
margin-bottom: 10px;
line-height: 1.3;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.survey-description {
color: #666;
line-height: 1.5;
margin-bottom: 20px;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
.survey-meta {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
font-size: 0.9em;
color: #888;
}
.survey-stats {
display: flex;
gap: 20px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.stat-item {
display: flex;
align-items: center;
gap: 8px;
font-size: 0.9em;
color: #666;
}
.stat-item i {
color: var(--survey-primary);
width: 16px;
}
.survey-progress {
margin-bottom: 20px;
}
.progress-bar {
width: 100%;
height: 8px;
background: #e0e0e0;
border-radius: 4px;
overflow: hidden;
margin-bottom: 8px;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, var(--survey-primary) 0%, var(--survey-primary-dark) 100%);
border-radius: 4px;
transition: width 0.6s ease;
}
.progress-text {
font-size: 0.8em;
color: #666;
text-align: right;
}
.survey-actions {
display: flex;
gap: 10px;
align-items: center;
}
.btn-participate {
flex: 1;
padding: 12px 20px;
background: var(--survey-primary);
color: white;
text-decoration: none;
border-radius: 10px;
text-align: center;
font-weight: 600;
transition: all 0.3s ease;
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.btn-participate:hover {
background: var(--survey-primary-dark);
transform: translateY(-2px);
color: white;
box-shadow: 0 5px 15px rgba(170, 32, 255, 0.3);
}
.btn-share {
padding: 12px;
background: #f8f9fa;
color: #666;
border: 1px solid #e0e0e0;
border-radius: 10px;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
}
.btn-share:hover {
background: #e9ecef;
color: #333;
transform: translateY(-2px);
}
.section-footer {
text-align: center;
margin-top: 40px;
}
.btn-more {
display: inline-flex;
align-items: center;
gap: 10px;
padding: 15px 30px;
background: var(--survey-primary);
color: white;
text-decoration: none;
border-radius: 50px;
font-weight: 600;
transition: all 0.3s ease;
box-shadow: 0 5px 15px rgba(170, 32, 255, 0.2);
}
.btn-more:hover {
background: var(--survey-primary-dark);
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(170, 32, 255, 0.3);
color: white;
}
.btn-more i {
transition: transform 0.3s ease;
}
.btn-more:hover i {
transform: translateX(5px);
}
.empty-state {
text-align: center;
padding: 80px 20px;
color: #666;
}
.empty-state i {
font-size: 4em;
color: #ddd;
margin-bottom: 20px;
display: block;
}
.empty-state h3 {
font-size: 1.5em;
margin-bottom: 10px;
color: #333;
}
.empty-state p {
font-size: 1em;
line-height: 1.5;
}
/* 모달 스타일 */
.survey-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;
}
.survey-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: 600px;
background: white;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
animation: slideUp 0.4s ease-out;
max-height: 90vh;
overflow-y: auto;
}
.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-title {
font-size: 1.5em;
font-weight: 600;
color: #333;
margin: 0 0 10px 0;
line-height: 1.3;
}
.modal-meta {
display: flex;
gap: 20px;
font-size: 0.9em;
color: #666;
}
.modal-body {
padding: 30px;
}
.modal-description {
color: #666;
line-height: 1.6;
margin-bottom: 30px;
}
.modal-stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.modal-stats .stat-item {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
padding: 20px;
background: #f8f9fa;
border-radius: 12px;
gap: 8px;
}
.modal-stats .stat-item i {
font-size: 1.5em;
color: var(--survey-primary);
}
.stat-label {
font-size: 0.9em;
color: #666;
}
.stat-value {
font-size: 1.2em;
font-weight: 600;
color: #333;
}
.progress-section {
margin-bottom: 20px;
}
.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;
}
.modal-footer .btn-participate {
padding: 12px 24px;
border-radius: 8px;
flex: none;
}
/* 반응형 */
@media (max-width: 768px) {
.survey-section {
padding: 60px 0;
}
.survey-section .section-header h2 {
font-size: 2em;
}
.survey-grid {
grid-template-columns: 1fr;
gap: 20px;
}
.survey-card {
padding: 25px;
}
.survey-stats {
flex-direction: column;
gap: 10px;
}
.survey-actions {
flex-direction: column;
}
.modal-content {
width: 95%;
margin: 20px;
}
.modal-header,
.modal-body,
.modal-footer {
padding: 20px;
}
.modal-stats {
grid-template-columns: 1fr;
gap: 15px;
}
.modal-footer {
flex-direction: column;
}
.modal-footer .btn-participate,
.btn-cancel {
width: 100%;
text-align: center;
}
}
/* 애니메이션 */
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slideUp {
from {
opacity: 0;
transform: translate(-50%, -40%);
}
to {
opacity: 1;
transform: translate(-50%, -50%);
}
}
.survey-card {
animation: fadeInUp 0.6s ease forwards;
}
.survey-card:nth-child(even) {
animation-delay: 0.1s;
}
.survey-card:nth-child(odd) {
animation-delay: 0.2s;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* 호버 효과 강화 */
.survey-card:hover .survey-title {
color: var(--survey-primary);
}
.survey-card:hover .stat-item i {
transform: scale(1.1);
}
.survey-card:hover .progress-fill {
box-shadow: 0 0 10px rgba(170, 32, 255, 0.3);
}
@@ -0,0 +1,333 @@
function initSurveyListModule(moduleId) {
const moduleElement = document.getElementById(moduleId);
if (!moduleElement || moduleElement.classList.contains('initialized')) return;
const surveySection = moduleElement.querySelector('.survey-section');
const surveyGrid = moduleElement.querySelector('.survey-grid');
const modal = moduleElement.querySelector('.survey-modal');
const modalOverlay = modal.querySelector('.modal-overlay');
const modalClose = modal.querySelector('.modal-close');
const modalTitle = modal.querySelector('.modal-title');
const modalAuthor = modal.querySelector('.modal-author');
const modalDeadline = modal.querySelector('.modal-deadline');
const modalDescription = modal.querySelector('.modal-description');
const questionsCount = modal.querySelector('.questions-count');
const participantsCount = modal.querySelector('.participants-count');
const estimatedTime = modal.querySelector('.estimated-time');
const progressFill = modal.querySelector('.progress-fill');
const progressText = modal.querySelector('.progress-text');
const btnParticipate = modal.querySelector('.modal-footer .btn-participate');
const btnCancel = modal.querySelector('.btn-cancel');
// 설문 데이터 가져오기
const surveysData = JSON.parse(surveySection.dataset.surveys || '[]');
// 설문 카드 렌더링
function renderSurveyCards() {
if (!surveysData.length) return;
const cardsHTML = surveysData.map(survey => `
<div class="survey-card" data-survey-id="${survey.id}" style="--theme-color: ${survey.theme_color}">
<div class="survey-card-header">
<h3 class="survey-title">${survey.title}</h3>
${survey.description ? `<p class="survey-description">${survey.description}</p>` : ''}
</div>
<div class="survey-meta">
<span><i class="fa fa-calendar"></i> ${survey.days_left}일 남음</span>
<span><i class="fa fa-user"></i> ${survey.created_by}</span>
</div>
<div class="survey-stats">
<div class="stat-item">
<i class="fa fa-question-circle"></i>
<span>${survey.questions_count}개 질문</span>
</div>
<div class="stat-item">
<i class="fa fa-users"></i>
<span>${formatNumber(survey.response_count)}명 참여</span>
</div>
<div class="stat-item">
<i class="fa fa-clock"></i>
<span>약 ${survey.questions_count}분</span>
</div>
</div>
${survey.max_responses ? `
<div class="survey-progress">
<div class="progress-bar">
<div class="progress-fill" style="width: ${survey.progress}%"></div>
</div>
<div class="progress-text">
${formatNumber(survey.response_count)} / ${formatNumber(survey.max_responses)}
</div>
</div>
` : ''}
<div class="survey-actions">
<button class="btn-participate" data-survey-id="${survey.id}">
<i class="fa fa-play"></i> 참여하기
</button>
<button class="btn-share" data-survey-id="${survey.id}" data-title="${survey.title}">
<i class="fa fa-share"></i>
</button>
</div>
</div>
`).join('');
surveyGrid.innerHTML = cardsHTML;
}
// 모달 열기
function openModal(surveyId) {
const survey = surveysData.find(s => s.id == surveyId);
if (!survey) return;
// 모달 내용 업데이트
modalTitle.textContent = survey.title;
modalAuthor.textContent = survey.created_by;
modalDeadline.textContent = `${survey.days_left}일 남음`;
modalDescription.textContent = survey.description || '설문에 대한 설명이 없습니다.';
questionsCount.textContent = `${survey.questions_count}`;
participantsCount.textContent = `${formatNumber(survey.response_count)}`;
estimatedTime.textContent = `${survey.questions_count}`;
// 진행률 업데이트
if (survey.max_responses) {
progressFill.style.width = `${survey.progress}%`;
progressText.textContent = `${formatNumber(survey.response_count)} / ${formatNumber(survey.max_responses)}명 참여`;
modal.querySelector('.progress-section').style.display = 'block';
} else {
modal.querySelector('.progress-section').style.display = 'none';
}
// 참여하기 버튼 링크 설정
btnParticipate.href = `${window.location.origin}/theme/rd.lwd/survey_page.php?sv_id=${survey.id}`;
// 모달 표시
modal.classList.add('show');
document.body.style.overflow = 'hidden';
}
// 모달 닫기
function closeModal() {
modal.classList.remove('show');
document.body.style.overflow = '';
}
// 설문 공유
function shareSurvey(surveyId, title) {
const url = `${window.location.origin}/theme/rd.lwd/rb.custom/survey_form/survey_page.php?sv_id=${surveyId}`;
if (navigator.share) {
// Web Share API 지원시
navigator.share({
title: title,
text: '설문에 참여해보세요!',
url: url
}).catch(err => {
console.log('공유 취소:', err);
});
} else {
// 클립보드에 복사
if (navigator.clipboard) {
navigator.clipboard.writeText(url).then(() => {
showNotification('설문 링크가 클립보드에 복사되었습니다!', 'success');
}).catch(() => {
// 클립보드 복사 실패시 프롬프트로 표시
prompt('설문 링크를 복사하세요:', url);
});
} else {
// 클립보드 API 미지원시 프롬프트로 표시
prompt('설문 링크를 복사하세요:', url);
}
}
}
// 알림 표시
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' : 'info-circle'}"></i>
<span>${message}</span>
</div>
`;
// 스타일 추가
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: ${type === 'success' ? '#28a745' : '#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;
`;
document.body.appendChild(notification);
setTimeout(() => {
notification.style.animation = 'slideOutRight 0.3s ease';
setTimeout(() => {
notification.remove();
}, 300);
}, 3000);
}
// 숫자 포맷팅
function formatNumber(num) {
return new Intl.NumberFormat('ko-KR').format(num);
}
// 이벤트 리스너 등록
function bindEvents() {
// 설문 카드 클릭 (모달 열기)
surveyGrid.addEventListener('click', (e) => {
const card = e.target.closest('.survey-card');
if (card && !e.target.closest('.survey-actions')) {
const surveyId = card.dataset.surveyId;
openModal(surveyId);
}
});
// 참여하기 버튼 클릭
surveyGrid.addEventListener('click', (e) => {
if (e.target.closest('.btn-participate')) {
e.stopPropagation();
const surveyId = e.target.closest('.btn-participate').dataset.surveyId;
window.location.href = `${window.location.origin}/theme/rd.lwd/rb.custom/survey_form/survey_page.php?sv_id=${surveyId}`;
}
});
// 공유 버튼 클릭
surveyGrid.addEventListener('click', (e) => {
if (e.target.closest('.btn-share')) {
e.stopPropagation();
const shareBtn = e.target.closest('.btn-share');
const surveyId = shareBtn.dataset.surveyId;
const title = shareBtn.dataset.title;
shareSurvey(surveyId, title);
}
});
// 모달 닫기 이벤트
modalClose.addEventListener('click', closeModal);
btnCancel.addEventListener('click', closeModal);
modalOverlay.addEventListener('click', closeModal);
// ESC 키로 모달 닫기
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && modal.classList.contains('show')) {
closeModal();
}
});
// 카드 호버 효과
surveyGrid.addEventListener('mouseenter', (e) => {
const card = e.target.closest('.survey-card');
if (card) {
const themeColor = card.style.getPropertyValue('--theme-color') || '#AA20FF';
card.style.setProperty('--survey-primary', themeColor);
}
}, true);
}
// 스크롤 애니메이션
function initScrollAnimation() {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.animationPlayState = 'running';
}
});
}, {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
});
// 카드들에 애니메이션 적용
setTimeout(() => {
const cards = surveyGrid.querySelectorAll('.survey-card');
cards.forEach((card, index) => {
card.style.animationDelay = `${index * 0.1}s`;
card.style.animationPlayState = 'paused';
observer.observe(card);
});
}, 100);
}
// 진행률 바 애니메이션
function animateProgressBars() {
const progressBars = surveyGrid.querySelectorAll('.progress-fill');
progressBars.forEach(bar => {
const width = bar.style.width;
bar.style.width = '0%';
setTimeout(() => {
bar.style.width = width;
}, 500);
});
}
// 초기화
function init() {
renderSurveyCards();
bindEvents();
initScrollAnimation();
// 진행률 바 애니메이션 (약간의 지연 후)
setTimeout(animateProgressBars, 800);
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;
}
`;
// 스타일 추가 (한 번만)
if (!document.getElementById('survey-list-module-styles')) {
const styleSheet = document.createElement('style');
styleSheet.id = 'survey-list-module-styles';
styleSheet.textContent = additionalStyles;
document.head.appendChild(styleSheet);
}
@@ -0,0 +1,155 @@
<?php
if (!defined('_GNUBOARD_')) exit;
// 설문 관리 라이브러리 로드
include_once(G5_ADMIN_PATH.'/survey_manage/lib/survey.lib.php');
// 설문 데이터 가져오기
$limit = 6; // 표시할 설문 수
$where = " WHERE sv_status = 'active' ";
$where .= " AND DATE(sv_start_date) <= CURDATE() ";
$where .= " AND DATE(sv_end_date) >= CURDATE() ";
$sql = " SELECT * FROM survey_master $where ORDER BY sv_created_at DESC LIMIT $limit ";
$result = sql_query($sql);
$survey_data = array();
while ($row = sql_fetch_array($result)) {
$questions_count = sql_fetch("SELECT COUNT(*) as cnt FROM survey_questions WHERE sv_id = '{$row['sv_id']}'")['cnt'];
$response_count = get_survey_response_count($row['sv_id'], 'completed');
$max_responses = $row['sv_max_responses'];
$progress = $max_responses ? ($response_count / $max_responses) * 100 : 0;
$days_left = ceil((strtotime($row['sv_end_date']) - time()) / (60 * 60 * 24));
$survey_data[] = array(
'id' => $row['sv_id'],
'title' => get_text($row['sv_title']),
'description' => get_text(cut_str(strip_tags($row['sv_description']), 100)),
'questions_count' => $questions_count,
'response_count' => $response_count,
'max_responses' => $max_responses,
'progress' => min($progress, 100),
'days_left' => $days_left,
'theme_color' => $row['sv_theme_color'] ?: '#AA20FF',
'created_by' => $row['sv_created_by']
);
}
$survey_json = json_encode($survey_data, JSON_UNESCAPED_UNICODE);
// CSS와 JS 파일의 버전을 파일 수정 시간으로 자동 갱신
$module_css_path = G5_THEME_PATH.'/rb.custom/survey_list/module.css';
$module_js_path = G5_THEME_PATH.'/rb.custom/survey_list/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_list_module_'.uniqid();
?>
<!-- 모듈의 가장 바깥 요소에 고유 ID를 부여합니다. -->
<div id="<?php echo $module_id; ?>" class="survey-list-module">
<section class="survey-section" data-surveys='<?php echo htmlspecialchars($survey_json, ENT_QUOTES, 'UTF-8'); ?>'>
<div class="container">
<div class="section-header">
<span class="subtitle">Survey</span>
<h2>설문조사</h2>
<p>다양한 설문에 참여하고 소중한 의견을 나눠주세요</p>
</div>
<?php if (!empty($survey_data)): ?>
<div class="survey-grid">
<!-- JS로 설문 카드가 생성될 영역 -->
</div>
<div class="section-footer">
<a href="<?php echo G5_THEME_URL; ?>/rb.custom/survey_list/survey_list_page.php" class="btn-more">
<span>모든 설문 보기</span>
<i class="fa fa-arrow-right"></i>
</a>
</div>
<?php else: ?>
<div class="empty-state">
<i class="fa fa-poll"></i>
<h3>진행중인 설문이 없습니다</h3>
<p>새로운 설문이 등록되면 알려드리겠습니다.</p>
</div>
<?php endif; ?>
</div>
</section>
<!-- 설문 상세 모달 -->
<div class="survey-modal">
<div class="modal-overlay"></div>
<div class="modal-content">
<button type="button" class="modal-close">&times;</button>
<div class="modal-header">
<h3 class="modal-title"></h3>
<div class="modal-meta">
<span class="modal-author"></span>
<span class="modal-deadline"></span>
</div>
</div>
<div class="modal-body">
<p class="modal-description"></p>
<div class="modal-stats">
<div class="stat-item">
<i class="fa fa-question-circle"></i>
<span class="stat-label">질문 수</span>
<span class="stat-value questions-count"></span>
</div>
<div class="stat-item">
<i class="fa fa-users"></i>
<span class="stat-label">참여자</span>
<span class="stat-value participants-count"></span>
</div>
<div class="stat-item">
<i class="fa fa-clock"></i>
<span class="stat-label">예상 시간</span>
<span class="stat-value estimated-time"></span>
</div>
</div>
<div class="progress-section">
<div class="progress-bar">
<div class="progress-fill"></div>
</div>
<div class="progress-text"></div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn-cancel">취소</button>
<a href="#" class="btn-participate">참여하기</a>
</div>
</div>
</div>
</div>
<!-- 이 모듈에 필요한 CSS 파일을 불러옵니다. -->
<link rel="stylesheet" href="<?php echo G5_THEME_URL; ?>/rb.custom/survey_list/module.css?ver=<?php echo $module_css_ver; ?>">
<script>
(function() {
const currentModuleId = '<?php echo $module_id; ?>';
const initFunctionName = 'initSurveyListModule';
const scriptId = 'survey-list-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_list/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,199 @@
<?php
include_once('../../../../common.php');
$g5['title'] = '설문조사';
include_once(G5_THEME_PATH.'/head.sub.php');
?>
<style>
/* 페이지 전용 스타일 */
body {
background: #f8f9fa;
}
.page-container {
min-height: 100vh;
padding-top: 100px; /* 헤더 높이만큼 */
}
.page-header {
background: linear-gradient(135deg, #AA20FF 0%, #8A1ACC 100%);
color: white;
padding: 80px 0;
margin-bottom: 0;
text-align: center;
}
.page-header h1 {
font-size: 3em;
font-weight: 700;
margin-bottom: 20px;
}
.page-header p {
font-size: 1.2em;
opacity: 0.9;
max-width: 600px;
margin: 0 auto;
}
.search-section {
background: white;
padding: 40px 0;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
margin-bottom: 40px;
}
.search-form {
max-width: 600px;
margin: 0 auto;
display: flex;
gap: 15px;
align-items: center;
}
.search-input {
flex: 1;
padding: 15px 20px;
border: 2px solid #e0e0e0;
border-radius: 50px;
font-size: 1em;
transition: all 0.3s ease;
}
.search-input:focus {
outline: none;
border-color: #AA20FF;
box-shadow: 0 0 0 3px rgba(170, 32, 255, 0.1);
}
.search-btn {
padding: 15px 30px;
background: #AA20FF;
color: white;
border: none;
border-radius: 50px;
font-size: 1em;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
}
.search-btn:hover {
background: #8A1ACC;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(170, 32, 255, 0.3);
}
.filter-section {
margin-bottom: 40px;
}
.filter-tabs {
display: flex;
justify-content: center;
gap: 10px;
flex-wrap: wrap;
}
.filter-tab {
padding: 10px 20px;
background: white;
color: #666;
text-decoration: none;
border-radius: 25px;
border: 2px solid #e0e0e0;
transition: all 0.3s ease;
font-weight: 500;
}
.filter-tab:hover,
.filter-tab.active {
background: #AA20FF;
color: white;
border-color: #AA20FF;
text-decoration: none;
}
.surveys-section {
padding: 0 0 80px 0;
}
/* 반응형 */
@media (max-width: 768px) {
.page-container {
padding-top: 80px;
}
.page-header {
padding: 60px 0;
}
.page-header h1 {
font-size: 2.2em;
}
.search-form {
flex-direction: column;
padding: 0 20px;
}
.search-input {
width: 100%;
}
.filter-tabs {
padding: 0 20px;
}
}
</style>
<div class="page-container">
<!-- 페이지 헤더 -->
<div class="page-header">
<div class="container">
<h1><i class="fa fa-poll"></i> 설문조사</h1>
<p>다양한 설문에 참여하고 소중한 의견을 나눠주세요</p>
</div>
</div>
<!-- 검색 섹션 -->
<div class="search-section">
<div class="container">
<form method="get" class="search-form">
<input type="text"
name="stx"
value="<?php echo htmlspecialchars($_GET['stx'] ?? ''); ?>"
placeholder="관심있는 설문을 검색해보세요..."
class="search-input">
<button type="submit" class="search-btn">
<i class="fa fa-search"></i> 검색
</button>
</form>
</div>
</div>
<!-- 필터 섹션 -->
<div class="filter-section">
<div class="container">
<div class="filter-tabs">
<a href="?" class="filter-tab <?php echo !isset($_GET['category']) ? 'active' : ''; ?>">전체</a>
<a href="?category=customer" class="filter-tab <?php echo ($_GET['category'] ?? '') == 'customer' ? 'active' : ''; ?>">고객서비스</a>
<a href="?category=product" class="filter-tab <?php echo ($_GET['category'] ?? '') == 'product' ? 'active' : ''; ?>">제품개발</a>
<a href="?category=marketing" class="filter-tab <?php echo ($_GET['category'] ?? '') == 'marketing' ? 'active' : ''; ?>">마케팅</a>
</div>
</div>
</div>
<!-- 설문 목록 섹션 (rb.custom 모듈 사용) -->
<div class="surveys-section">
<?php
// 전체 설문 목록을 위한 커스텀 모듈 (확장된 버전)
include_once(G5_THEME_PATH.'/rb.custom/survey_list/module.php');
?>
</div>
</div>
<?php
include_once(G5_THEME_PATH.'/tail.sub.php');
?>
@@ -0,0 +1,75 @@
<?php
if (!defined('_GNUBOARD_')) exit;
/**
* 게시판 최신글 또는 콘텐츠 페이지를 탭으로 생성하는 함수
*
* @param array $tabs_config 탭 설정 배열
* @return string 생성된 탭 HTML
*/
function rb_tabs($tabs_config) {
// 💡 [복원] 설정값이 유효한지 확인하는 필수 코드
if (!is_array($tabs_config) || empty($tabs_config)) {
return '';
}
// 💡 [복원] 고유 ID 생성 (한 페이지에 여러 탭 모듈이 있어도 충돌 방지)
$module_id = 'rb-tab-module-' . uniqid();
// 💡 [복원] 이 모듈에 필요한 CSS와 JS를 동적으로 추가합니다.
add_stylesheet('<link rel="stylesheet" href="'.G5_THEME_URL.'/rb.custom/tabs/tab_style.css?ver='.G5_SERVER_TIME.'">', 0);
add_javascript('<script src="'.G5_THEME_URL.'/rb.custom/tabs/tab_script.js?ver='.G5_SERVER_TIME.'"></script>', 10);
// 💡 [복원] HTML 출력을 안전하게 버퍼에 담기 시작
ob_start();
?>
<div class="rb-tab-module" id="<?php echo $module_id; ?>">
<!-- 1. 탭 네비게이션 -->
<ul class="rb-tab-nav">
<?php foreach ($tabs_config as $index => $tab): ?>
<?php
$tab_id = $tab['type'] . '-' . $tab['id'];
$is_active = ($index === 0) ? 'active' : '';
?>
<li><a href="#<?php echo $tab_id; ?>" class="<?php echo $is_active; ?>"><?php echo htmlspecialchars($tab['title']); ?></a></li>
<?php endforeach; ?>
</ul>
<!-- 2. 탭 콘텐츠 -->
<div class="rb-tab-content-wrapper">
<?php foreach ($tabs_config as $index => $tab): ?>
<?php
$tab_id = $tab['type'] . '-' . $tab['id'];
$is_active = ($index === 0) ? 'active' : '';
?>
<div id="<?php echo $tab_id; ?>" class="rb-tab-content <?php echo $is_active; ?>">
<?php
// 💡 [핵심] 탭 타입에 따라 다른 내용을 불러옵니다.
switch ($tab['type']) {
case 'board':
// 💡 [개선] 타입이 'board'이면 탭 전용 최신글 스킨(rb_tab_basic)을 불러옵니다.
$options = $tab['options'] ?? [];
$rows = $options['rows'] ?? 5;
$subject_len = $options['subject_len'] ?? 40;
echo latest('rb_tab_basic', $tab['id'], $rows, $subject_len);
break;
case 'content':
// 타입이 'content'이면 g5_content 테이블에서 내용을 불러옵니다.
$co = get_content_db($tab['id']);
if ($co) {
echo conv_content($co['co_content'], $co['co_html']);
} else {
echo '<div class="no-posts">콘텐츠('.htmlspecialchars($tab['id']).')를 찾을 수 없습니다.</div>';
}
break;
}
?>
</div>
<?php endforeach; ?>
</div>
</div>
<?php
// 💡 [복원] 버퍼에 담긴 HTML을 반환하고 버퍼를 비웁니다.
return ob_get_clean();
}
@@ -0,0 +1,24 @@
document.addEventListener('DOMContentLoaded', function() {
const tabModules = document.querySelectorAll('.rb-tab-module');
tabModules.forEach(module => {
const navLinks = module.querySelectorAll('.rb-tab-nav a');
const contentPanes = module.querySelectorAll('.rb-tab-content');
navLinks.forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
navLinks.forEach(item => item.classList.remove('active'));
contentPanes.forEach(pane => pane.classList.remove('active'));
this.classList.add('active');
const targetId = this.getAttribute('href');
const targetPane = module.querySelector(targetId);
if (targetPane) {
targetPane.classList.add('active');
}
});
});
});
});
@@ -0,0 +1,58 @@
/* 탭 모듈 전체 */
.rb-tab-module {
border: 1px solid #e9e9e9;
border-radius: 8px;
overflow: hidden;
background: #fff;
margin-bottom: 20px;
}
/* 탭 네비게이션 */
.rb-tab-nav {
display: flex;
list-style: none;
padding: 0;
margin: 0;
border-bottom: 1px solid #e9e9e9;
background-color: #f9f9f9;
}
.rb-tab-nav a {
display: block;
padding: 12px 20px;
text-decoration: none;
color: #555;
font-weight: 500;
border-bottom: 3px solid transparent;
transition: all 0.2s ease-in-out;
}
.rb-tab-nav a.active {
color: #0056b3;
font-weight: 700;
border-bottom-color: #0056b3;
background-color: #fff;
}
/* 탭 콘텐츠 */
.rb-tab-content-wrapper {
padding: 15px;
position: relative;
}
.rb-tab-content {
display: none;
}
.rb-tab-content.active {
display: block;
}
.rb-tab-content .no-posts {
color: #999;
padding: 30px 0;
text-align: center;
}
/* latest 스킨(basic)을 탭 내부에 맞게 스타일 조정 */
.rb-tab-content .lat {
border: none;
padding: 0;
}
.rb-tab-content .lat ul {
margin: 0;
}
@@ -0,0 +1,279 @@
/*
* ==========================================================================
* 💡 tech_section 모듈 전용 스타일시트
* ==========================================================================
*/
/* --- 1. 섹션 기본 스타일 --- */
.products-trend-section {
width: 100%;
padding: 80px 0;
background-color: #ffffff; /* 또는 원하는 배경색 */
}
/* --- 2. 섹션 헤더 (제목, 부제) --- */
.section-header {
text-align: center;
margin-bottom: 50px;
}
.section-header .subtitle {
font-size: 16px;
font-weight: 700;
color: #0056b3; /* 포인트 색상 */
margin-bottom: 10px;
display: block;
}
.section-header h2 {
font-size: 36px;
font-weight: 900;
color: #25282B; /* 기본 텍스트 색상 */
margin-bottom: 15px;
line-height: 1.4;
}
.section-header p {
font-size: 16px;
line-height: 1.7;
color: #666;
max-width: 600px;
margin: 0 auto;
}
/* --- 3. 제품 카드 그리드 --- */
.product-grid {
display: grid;
/* 💡 화면 크기에 따라 1~3개의 컬럼으로 자동 조정됩니다. */
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
}
/* --- 4. 개별 제품 카드 --- */
.product-card-trend {
background-color: #fff;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
transition: transform 0.3s ease, box-shadow 0.3s ease;
cursor: pointer;
}
.product-card-trend:hover {
transform: translateY(-5px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
}
/* --- 5. 카드 내부 요소 --- */
.product-image-trend {
width: 100%;
/* 💡 이미지 비율을 4:3으로 유지합니다. */
aspect-ratio: 4 / 3;
overflow: hidden;
}
.product-image-trend.is-patent {
padding: 20px;
background-color: #f0f0f0;
}
.product-image-trend img {
width: 100%;
height: 100%;
object-fit: cover; /* 이미지가 잘리지 않고 꽉 차도록 설정 */
transition: transform 0.4s ease;
}
.product-card-trend:hover .product-image-trend img {
transform: scale(1.05);
}
.product-info-trend {
padding: 25px;
}
.product-info-trend h3 {
font-size: 1.2rem;
font-weight: 700;
margin-bottom: 10px;
color: #333;
}
.product-info-trend p {
font-size: 0.95rem;
color: #666;
line-height: 1.6;
}
/* --- 9. 스크롤 애니메이션 (style_prestige_1.css에서 가져옴) --- */
.reveal-up, .reveal-fade {
opacity: 0;
transition: opacity 0.8s ease-out, transform 0.8s ease-out;
}
.reveal-up { transform: translateY(40px); }
.reveal-fade { transform: scale(0.95); }
.reveal-up.is-revealed, .reveal-fade.is-revealed {
opacity: 1;
transform: none;
}
/* --- 10. [모듈 전용] 이미지 팝업 모달 스타일 (개선) --- */
.image-modal {
display: none; /* 기본적으로 숨김 */
position: fixed;
z-index: 1050;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto; /* 내용이 클 경우 스크롤 */
background-color: rgba(0, 0, 0, 0.85);
justify-content: center;
align-items: center;
padding: 40px 20px; /* 화면 가장자리와 여백 */
opacity: 0;
transition: opacity 0.3s ease;
}
.image-modal.is-active {
display: flex;
opacity: 1;
}
.modal-content {
position: relative;
background-color: #fff;
margin: auto;
padding: 0;
border-radius: 8px;
width: auto; /* 너비를 자동으로 설정 */
max-width: 100%; /* 화면 너비를 넘지 않도록 설정 */
box-shadow: 0 5px 15px rgba(0,0,0,.5);
animation: modal-slide-down 0.4s ease-out;
display: flex; /* 내부 요소 정렬을 위해 flex 사용 */
flex-direction: column; /* 이미지와 텍스트를 세로로 쌓음 */
}
@keyframes modal-slide-down {
from {
transform: translateY(-50px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
.modal-content .modal-image {
display: block;
max-width: 90vw;
max-height: 80vh;
object-fit: contain;
width: auto;
height: auto;
}
.modal-info {
padding: 20px 25px;
max-width: 800px; /* 텍스트 영역의 최대 너비는 제한 */
width: 100%;
}
.modal-info .modal-title {
font-size: 1.5rem;
font-weight: 700;
color: #333;
margin: 0 0 10px 0;
}
.modal-info .modal-desc {
font-size: 1rem;
color: #666;
line-height: 1.6;
margin: 0;
}
.close-btn {
color: #fff;
position: absolute;
top: 15px;
right: 25px;
font-size: 35px;
font-weight: bold;
cursor: pointer;
transition: color 0.2s;
text-shadow: 0 1px 3px rgba(0,0,0,0.5);
z-index: 10;
}
.close-btn:hover,
.close-btn:focus {
color: #ccc;
text-decoration: none;
}
/* --- 11. [개선] 가독성 향상을 위한 줄바꿈 처리 --- */
.section-header h2,
.section-header p,
.product-info-trend h3,
.product-info-trend p,
.item-info h3,
.item-info p {
word-break: keep-all;
overflow-wrap: break-word;
}
/* --- 12. [개선] 카드 설명 텍스트 잘림 방지 및 높이 고정 --- */
.product-info-trend p,
.item-info p {
height: 3.04rem;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
line-height: 1.6;
}
/* -------------------------------------------------- */
/* 💡 [최종 수정] 모달 스크롤바 완전 숨김 처리 */
/* -------------------------------------------------- */
/* 1. 기본 상태: 스크롤바를 완전히 투명하게 만듭니다. */
.rb-modal-overlay,
.rb-modal-body {
/* Firefox: 스크롤바 색상을 양쪽 모두 투명하게 설정 */
scrollbar-color: transparent transparent;
scrollbar-width: thin;
transition: scrollbar-color 0.3s ease; /* 부드러운 효과를 위해 추가 */
}
.rb-modal-overlay::-webkit-scrollbar,
.rb-modal-body::-webkit-scrollbar {
width: 10px;
}
.rb-modal-overlay::-webkit-scrollbar-track,
.rb-modal-body::-webkit-scrollbar-track {
background: transparent;
}
.rb-modal-overlay::-webkit-scrollbar-thumb,
.rb-modal-body::-webkit-scrollbar-thumb {
/* Webkit: 스크롤바 막대를 투명하게 */
background-color: transparent;
border-radius: 10px;
border: 3px solid transparent;
background-clip: padding-box;
transition: background-color 0.3s ease;
}
/* 2. :hover 상태: 마우스를 올리면 스크롤바가 나타납니다. */
.rb-modal-overlay:hover,
.rb-modal-body:hover {
/* Firefox: 스크롤바 색상을 보이게 변경 */
scrollbar-color: rgba(0, 0, 0, 0.4) transparent;
}
.rb-modal-overlay:hover::-webkit-scrollbar-thumb,
.rb-modal-body:hover::-webkit-scrollbar-thumb {
/* Webkit: 스크롤바 막대 색상을 보이게 변경 */
background-color: rgba(0, 0, 0, 0.4);
}
@@ -0,0 +1,102 @@
// 💡 [핵심 수정] 모든 로직을 초기화 함수 안으로 옮기고, 모듈의 고유 ID를 인자로 받도록 변경합니다.
function initTechSectionModule(moduleId) {
// 💡 [핵심 수정] document가 아닌, 전달받은 moduleId를 기준으로 요소를 찾습니다.
const moduleElement = document.getElementById(moduleId);
if (!moduleElement) return;
// 이미 초기화된 모듈은 다시 실행하지 않습니다.
if (moduleElement.classList.contains('initialized')) {
return;
}
const section = moduleElement.querySelector('.products-trend-section');
const patentGrid = moduleElement.querySelector('.product-grid');
const modal = moduleElement.querySelector('.image-modal');
if (!section || !patentGrid || !modal) {
console.error('Module elements not found in:', moduleId);
return;
}
/**
* 그리드에 특허 카드를 렌더링하는 함수
*/
function renderGrid() {
const patentsJson = section.dataset.patents;
let patentsData;
try {
patentsData = JSON.parse(patentsJson);
} catch (e) {
patentGrid.innerHTML = '<p style="text-align:center; padding: 40px 0; color: #d9534f;">기술 자료를 불러오는 데 실패했습니다.</p>';
return;
}
if (!patentsData || patentsData.length === 0) {
patentGrid.innerHTML = '<p style="text-align:center; padding: 40px 0; color: #888;">표시할 기술 자료가 없습니다.</p>';
return;
}
const patentCardsHTML = patentsData.map(item => `
<div class="product-card-trend js-modal-trigger" data-title="${item.title}" data-desc="${item.description}" data-img="${item.image}">
<div class="product-image-trend is-patent">
<img src="${item.image}" alt="${item.title}">
</div>
<div class="product-info-trend">
<h3>${item.title}</h3>
<p>${item.description}</p>
</div>
</div>
`).join('');
patentGrid.innerHTML = patentCardsHTML;
}
/**
* 모달 관련 이벤트를 설정하는 함수
*/
function setupModalEvents() {
const modalImage = modal.querySelector('.modal-image');
const modalTitle = modal.querySelector('.modal-title');
const modalDesc = modal.querySelector('.modal-desc');
const closeBtn = modal.querySelector('.close-btn');
// 모달 열기 (이벤트 위임)
patentGrid.addEventListener('click', function(event) {
const card = event.target.closest('.js-modal-trigger');
if (card) {
modalImage.src = card.dataset.img;
modalTitle.textContent = card.dataset.title;
modalDesc.textContent = card.dataset.desc;
modal.classList.add('is-active');
}
});
// 모달 닫기 함수
function closeModal() {
modal.classList.remove('is-active');
}
// 이벤트 리스너 등록
closeBtn.addEventListener('click', closeModal);
modal.addEventListener('click', function(event) {
if (event.target === modal) closeModal();
});
// ESC 키 이벤트는 document에 등록해야 하므로, 모달이 활성화 상태일 때만 동작하도록 조건 추가
document.addEventListener('keydown', function(event) {
if (event.key === 'Escape' && modal.classList.contains('is-active')) {
closeModal();
}
});
}
// 함수 실행
renderGrid();
setupModalEvents();
// 초기화 완료 클래스 추가
moduleElement.classList.add('initialized');
}
// 💡 [핵심 수정] 다른 곳에서 이 함수를 다시 호출할 수 있도록 전역에 노출시킵니다.
window.initTechSectionModule = initTechSectionModule;
@@ -0,0 +1,97 @@
<?php
if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가
// --- 데이터 처리 ---
$patents_data = array();
$sql = " SELECT wr_id, wr_subject, wr_content FROM {$g5['write_prefix']}patents WHERE wr_is_comment = 0 ORDER BY wr_num, wr_reply LIMIT 3 ";
$result = sql_query($sql);
for ($i=0; $row=sql_fetch_array($result); $i++) {
$file = get_file('patents', $row['wr_id']);
$image_url = (isset($file[0]['path']) && isset($file[0]['file'])) ? $file[0]['path'].'/'.$file[0]['file'] : G5_THEME_URL.'/img/no_image.png';
$patents_data[] = array(
'id' => $row['wr_id'],
'title' => get_text($row['wr_subject']),
'description' => get_text(cut_str(strip_tags($row['wr_content']), 100)),
'image' => $image_url
);
}
$patents_json = json_encode($patents_data, JSON_UNESCAPED_UNICODE);
// 💡 [개선] CSS와 JS 파일의 버전을 파일 수정 시간으로 자동 갱신하여 캐시 관리를 최적화합니다.
$module_css_path = G5_THEME_PATH.'/rb.custom/tech_section/module.css';
$module_js_path = G5_THEME_PATH.'/rb.custom/tech_section/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 = 'tech_section_'.uniqid();
?>
<!-- 💡 [핵심 수정] 모듈의 가장 바깥 요소에 고유 ID를 부여합니다. -->
<div id="<?php echo $module_id; ?>" class="tech-section-module">
<!-- TECH: 기술력 및 특허 섹션 -->
<section class="products-trend-section" data-patents='<?php echo htmlspecialchars($patents_json, ENT_QUOTES, 'UTF-8'); ?>'>
<div class="container">
<div class="section-header">
<span class="subtitle">Technology</span>
<h2>문 하나에 사람을 담았습니다.</h2>
<p>당신의 공간이 더욱 안전하고 조용해지도록, 우리는 문 하나를 넘어서고 있습니다.</p>
</div>
<div class="product-grid">
<!-- JS로 특허증 카드 생성 -->
</div>
</div>
</section>
<!-- 💡 [핵심 수정] 모듈 내부에 모달 HTML을 포함시킵니다. -->
<div class="image-modal">
<div class="modal-content">
<span class="close-btn">&times;</span>
<img class="modal-image" src="" alt="제품 이미지">
<div class="modal-info">
<h3 class="modal-title"></h3>
<p class="modal-desc"></p>
</div>
</div>
</div>
</div>
<!-- 이 모듈에 필요한 CSS 파일을 불러옵니다. -->
<link rel="stylesheet" href="<?php echo G5_THEME_URL; ?>/rb.custom/tech_section/module.css?ver=<?php echo $module_css_ver; ?>">
<?php // 💡 [핵심 수정] AJAX로 로드될 때도 스크립트가 확실하게 실행되도록 동적 로딩 방식으로 변경합니다. ?>
<script>
(function() {
// 💡 [핵심 수정] 이 모듈의 고유 ID를 자바스크립트로 전달합니다.
const currentModuleId = '<?php echo $module_id; ?>';
const scriptId = 'tech-section-module-script';
// 스크립트가 이미 로드되었는지 확인 (중복 실행 방지)
if (document.getElementById(scriptId)) {
// 이미 로드되었다면, 초기화 함수만 다시 호출
if (typeof window.initTechSectionModule === 'function') {
window.initTechSectionModule(currentModuleId);
}
return;
}
const script = document.createElement('script');
script.id = scriptId;
script.src = '<?php echo G5_THEME_URL; ?>/rb.custom/tech_section/module.js?ver=<?php echo $module_js_ver; ?>';
script.async = true;
// 💡 [핵심 수정] 스크립트 로드가 완료되면, 고유 ID를 인자로 전달하여 초기화 함수를 호출합니다.
script.onload = () => {
if (typeof window.initTechSectionModule === 'function') {
window.initTechSectionModule(currentModuleId);
}
};
script.onerror = () => {
console.error('Failed to load tech_section/module.js');
};
document.head.appendChild(script);
})();
</script>