first commit 2
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* 파일명: contact-form-handler.js
|
||||
* 설명: 홈페이지 메인의 상담 신청 폼을 처리하고 팝업을 제어하는 스크립트
|
||||
*/
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// 모달 요소 선택 (contact.skin.php의 구조에 맞춤)
|
||||
const modal = document.getElementById('contact-modal');
|
||||
|
||||
// 모달이 없으면 실행 중단
|
||||
if (!modal) return;
|
||||
|
||||
const closeBtn = modal.querySelector('.modal-close');
|
||||
// modal 자체가 overlay 역할을 하므로 overlay 변수는 modal과 동일하거나 별도 배경 요소일 수 있음
|
||||
// contact.skin.php 구조상 id="contact-modal"이 overlay 역할을 함
|
||||
const overlay = modal;
|
||||
const contactForm = modal.querySelector('form');
|
||||
const formMessages = modal.querySelector('.form-message-area');
|
||||
const submitButton = contactForm ? contactForm.querySelector('button[type="submit"]') : null;
|
||||
|
||||
// 모달 열기 함수
|
||||
const openModal = (e) => {
|
||||
if (e) e.preventDefault();
|
||||
modal.classList.add('active'); // CSS에서 .active 클래스로 제어한다고 가정
|
||||
document.body.style.overflow = 'hidden';
|
||||
};
|
||||
|
||||
// 모달 닫기 함수
|
||||
const closeModal = () => {
|
||||
modal.classList.remove('active');
|
||||
document.body.style.overflow = '';
|
||||
};
|
||||
|
||||
// 1. 모달 열기 이벤트 위임 (어디서든 .open-contact-modal 클래스 클릭 시 열림)
|
||||
document.addEventListener('click', function(e) {
|
||||
const targetBtn = e.target.matches('.open-contact-modal') ? e.target : e.target.closest('.open-contact-modal');
|
||||
if (targetBtn) {
|
||||
openModal(e);
|
||||
}
|
||||
});
|
||||
|
||||
// 2. 모달 닫기 이벤트
|
||||
if (closeBtn) {
|
||||
closeBtn.addEventListener('click', closeModal);
|
||||
}
|
||||
|
||||
// 배경 클릭 시 닫기
|
||||
if (overlay) {
|
||||
overlay.addEventListener('click', function(e) {
|
||||
if (e.target === overlay) {
|
||||
closeModal();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ESC 키 누르면 닫기
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' && modal.classList.contains('active')) {
|
||||
closeModal();
|
||||
}
|
||||
});
|
||||
|
||||
// 3. 폼 제출 처리
|
||||
if (contactForm && submitButton) {
|
||||
contactForm.addEventListener('submit', function(e) {
|
||||
e.preventDefault(); // 기본 폼 전송 막기
|
||||
|
||||
const originalButtonText = submitButton.textContent;
|
||||
submitButton.disabled = true;
|
||||
submitButton.textContent = '전송 중...';
|
||||
|
||||
if (formMessages) {
|
||||
formMessages.style.display = 'none';
|
||||
formMessages.className = 'form-message-area'; // 초기화
|
||||
formMessages.innerHTML = '';
|
||||
}
|
||||
|
||||
const formData = new FormData(contactForm);
|
||||
const variables = {
|
||||
contact_subject: formData.get('contact_subject'),
|
||||
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,
|
||||
// [추가] 참조 이메일 설정
|
||||
// 'ADMIN_EMAIL' 키워드를 사용하면 서버(ajax_universal_send.php)에서
|
||||
// 실제 관리자 이메일 주소(config.php의 cf_admin_email)로 자동 치환합니다.
|
||||
cc_email: ['ADMIN_EMAIL'],
|
||||
// bcc_email: ['msbfox@naver.com'],
|
||||
variables: variables,
|
||||
onSuccess: function(response) {
|
||||
alert(response.message || '상담 신청이 성공적으로 접수되었습니다.');
|
||||
contactForm.reset();
|
||||
closeModal();
|
||||
},
|
||||
onError: function(errorMessage) {
|
||||
if (formMessages) {
|
||||
formMessages.innerHTML = `<div class="message error">${errorMessage || '오류가 발생했습니다.'}</div>`;
|
||||
formMessages.style.display = 'block';
|
||||
} else {
|
||||
alert(errorMessage || '오류가 발생했습니다.');
|
||||
}
|
||||
},
|
||||
onComplete: function() {
|
||||
submitButton.disabled = false;
|
||||
submitButton.textContent = originalButtonText;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
alert('메일 발송 모듈(universalMailer)이 로드되지 않았습니다.');
|
||||
submitButton.disabled = false;
|
||||
submitButton.textContent = originalButtonText;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가
|
||||
|
||||
// 📌 [핵심] 이 파일 하나만 include 하면 HTML, CSS, JS가 모두 로드되도록 구성합니다.
|
||||
|
||||
// 💡 [핵심 수정] 현재 스킨의 URL을 변수로 정의
|
||||
$contact_skin_url = G5_THEME_URL.'/skin/contact/'.$config['cf_theme'];
|
||||
|
||||
// 1. CSS 로드
|
||||
add_stylesheet('<link rel="stylesheet" href="'.$contact_skin_url.'/style.css">', 0);
|
||||
|
||||
// 2. JS 로드
|
||||
add_javascript('<script src="'.$contact_skin_url.'/universal-mailer.js"></script>', 10);
|
||||
add_javascript('<script src="'.$contact_skin_url.'/contact-form-handler.js"></script>', 11);
|
||||
$module_id = 'contact_modal_' . uniqid();
|
||||
?>
|
||||
|
||||
|
||||
<div id="contact-modal" class="contact-modal modal-overlay">
|
||||
<div class="modal-content">
|
||||
<button type="button" class="modal-close">×</button>
|
||||
<section class="cta-section">
|
||||
<div class="container">
|
||||
<div class="cta-content">
|
||||
<div class="cta-text">
|
||||
<h2>귀사의 기술을 알리고 싶으신가요?</h2>
|
||||
<p>월간 레이저 기술이 귀사의 비즈니스 파트너가 되어드립니다.</p>
|
||||
</div>
|
||||
<form name="fcontactform_<?php echo $module_id; ?>" id="fcontactform_<?php echo $module_id; ?>" class="contact-form" method="post" style="border-top: 1px solid #eee; padding-top: 20px;">
|
||||
<!-- <h3 style="margin-bottom:15px; font-size: 1.2rem;">상담 및 신청 접수</h3>-->
|
||||
<div class="form-message-area"></div>
|
||||
|
||||
<select name="contact_subject" required style="width:100%; height:45px; margin-bottom:10px; border:1px solid #ddd; text-align: center; text-align-last: center;">
|
||||
<option value="">문의 항목 선택</option>
|
||||
<option value="정기구독 신청">정기구독 신청 (연간/월간)</option>
|
||||
<option value="광고 게재 문의">광고 게재 및 단가 문의</option>
|
||||
<option value="독자 원고 투고">독자 원고 투고 접수</option>
|
||||
<option value="기타 문의">기타 문의사항</option>
|
||||
</select>
|
||||
|
||||
<div style="display: flex; gap: 10px;">
|
||||
<input type="text" name="contact_name" placeholder="업체명/성함" required style="flex:1;">
|
||||
<input type="tel" name="contact_hp" placeholder="연락처" required style="flex:1;">
|
||||
</div>
|
||||
<input type="email" name="contact_email" placeholder="이메일 주소" required>
|
||||
<textarea name="contact_message" placeholder="문의 내용 또는 배송지 주소(구독신청 시)를 입력해주세요." rows="3" required></textarea>
|
||||
<button type="submit" class="cta-button" style="background:#0056b3;">신청 및 문의하기</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 💡 [추가] 관리자 이메일 주소를 JavaScript 변수로 전달
|
||||
const ADMIN_EMAIL = "<?php echo $config['cf_admin_email']; ?>";
|
||||
</script>
|
||||
@@ -0,0 +1,228 @@
|
||||
/* --- 문의하기 팝업(모달) 전용 스타일 --- */
|
||||
|
||||
/* 💡 [핵심] 모든 스타일을 #contact-modal ID 하위로 한정하여 충돌 방지 */
|
||||
|
||||
#contact-modal {
|
||||
--primary-color: #ff6b6b;
|
||||
--secondary-color: #4ecdc4;
|
||||
--accent-color: #45b7d1;
|
||||
--tertiary-color: #96ceb4;
|
||||
--quaternary-color: #feca57;
|
||||
--text-primary: #2c3e50;
|
||||
--text-secondary: #7f8c8d;
|
||||
--text-light: #bdc3c7;
|
||||
--bg-primary: #ffffff;
|
||||
--bg-secondary: #f8f9fa;
|
||||
--bg-tertiary: #ecf0f1;
|
||||
--bg-card: #ffffff;
|
||||
--border-color: #e9ecef;
|
||||
--border-light: #f1f3f4;
|
||||
--shadow-sm: 0 2px 4px 0 rgb(0 0 0 / 0.1);
|
||||
--shadow-md: 0 4px 8px 0 rgb(0 0 0 / 0.1);
|
||||
--shadow-lg: 0 8px 16px 0 rgb(0 0 0 / 0.1);
|
||||
--shadow-xl: 0 16px 32px 0 rgb(0 0 0 / 0.1);
|
||||
--radius-sm: 0.5rem;
|
||||
--radius-md: 0.75rem;
|
||||
--radius-lg: 1rem;
|
||||
--radius-xl: 1.5rem;
|
||||
--spacing-xs: 0.5rem;
|
||||
--spacing-sm: 1rem;
|
||||
--spacing-md: 1.5rem;
|
||||
--spacing-lg: 2rem;
|
||||
--spacing-xl: 3rem;
|
||||
--spacing-2xl: 4rem;
|
||||
|
||||
font-family: 'Pretendard', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
/* 모달 오버레이 (최상위 요소) */
|
||||
#contact-modal.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 9999;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.3s ease, visibility 0.3s ease;
|
||||
}
|
||||
|
||||
#contact-modal.modal-overlay.active {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* 모달 컨텐츠 박스 */
|
||||
#contact-modal .modal-content {
|
||||
background-color: #fff;
|
||||
padding: 40px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 5px 20px rgba(0,0,0,0.2);
|
||||
width: 90%;
|
||||
max-width: 960px;
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
position: relative;
|
||||
transform: scale(0.9);
|
||||
transition: transform 0.3s ease;
|
||||
box-sizing: border-box; /* 패딩 포함 크기 계산 */
|
||||
}
|
||||
|
||||
#contact-modal.active .modal-content {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
/* 닫기 버튼 */
|
||||
#contact-modal .modal-close {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 20px;
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
color: #888;
|
||||
cursor: pointer;
|
||||
line-height: 1;
|
||||
padding: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
#contact-modal .modal-close:hover {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 컨테이너 (모달 내부) */
|
||||
#contact-modal .container {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
padding: 0; /* 모달 내부 패딩이 있으므로 제거 */
|
||||
}
|
||||
|
||||
/* CTA 섹션 스타일 재정의 */
|
||||
#contact-modal .cta-section {
|
||||
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%) !important;
|
||||
color: #fff !important;
|
||||
padding: 40px 20px !important; /* 패딩 조정 */
|
||||
text-align: center !important;
|
||||
border-radius: 8px; /* 모달 내부에서 둥글게 */
|
||||
}
|
||||
|
||||
#contact-modal .cta-content {
|
||||
text-align: center;
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#contact-modal .cta-text h2 {
|
||||
font-size: 2rem; /* 크기 조정 */
|
||||
font-weight: 700;
|
||||
margin-bottom: 1rem;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#contact-modal .cta-text p {
|
||||
font-size: 1rem;
|
||||
opacity: 0.9;
|
||||
margin-bottom: 2rem;
|
||||
line-height: 1.6;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* 폼 메시지 영역 */
|
||||
#contact-modal .form-message-area {
|
||||
padding: 15px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
display: none;
|
||||
text-align: center;
|
||||
color: #333; /* 기본 글자색 */
|
||||
}
|
||||
|
||||
#contact-modal .form-message-area.success {
|
||||
background-color: #d4edda;
|
||||
color: #155724;
|
||||
border: 1px solid #c3e6cb;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#contact-modal .form-message-area.error {
|
||||
background-color: #f8d7da;
|
||||
color: #721c24;
|
||||
border: 1px solid #f5c6cb;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 문의 폼 스타일 */
|
||||
#contact-modal .contact-form {
|
||||
background: #fff; /* 폼 배경 흰색 */
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
color: #333; /* 폼 내부 글자색 */
|
||||
text-align: left; /* 입력 필드 정렬 */
|
||||
}
|
||||
|
||||
#contact-modal .contact-form input,
|
||||
#contact-modal .contact-form select,
|
||||
#contact-modal .contact-form textarea {
|
||||
width: 100%;
|
||||
padding: 12px 15px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 15px;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
box-sizing: border-box;
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
#contact-modal .contact-form input:focus,
|
||||
#contact-modal .contact-form select:focus,
|
||||
#contact-modal .contact-form textarea:focus {
|
||||
outline: none;
|
||||
border-color: #2c3e50;
|
||||
box-shadow: 0 0 0 3px rgba(44, 62, 80, 0.1);
|
||||
}
|
||||
|
||||
#contact-modal .contact-form .cta-button {
|
||||
width: 100%;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
background-color: #0056b3;
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
padding: 15px;
|
||||
border-radius: 6px;
|
||||
font-weight: 700;
|
||||
transition: background-color 0.3s ease;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#contact-modal .contact-form .cta-button:hover {
|
||||
background-color: #004494;
|
||||
}
|
||||
|
||||
/* 반응형 스타일 */
|
||||
@media (max-width: 768px) {
|
||||
#contact-modal .modal-content {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
#contact-modal .cta-text h2 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
#contact-modal .contact-form {
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* 파일명: universal-mailer.js
|
||||
* 설명: 사이트 전체에서 사용할 수 있는 범용 메일 발송 클라이언트
|
||||
*/
|
||||
(function(window) {
|
||||
'use strict';
|
||||
|
||||
function UniversalMailer() {}
|
||||
|
||||
/**
|
||||
* AJAX를 통해 지정된 템플릿으로 이메일을 발송합니다.
|
||||
* @param {object} options - 메일 발송 옵션 객체
|
||||
* @param {string} options.template_code - 사용할 메일 템플릿의 고유 코드
|
||||
* @param {object} options.variables - 템플릿에 치환될 변수 객체 (예: {name: "홍길동"})
|
||||
* @param {string|string[]} [options.to_email] - 수신자 이메일. (문자열 또는 배열)
|
||||
* @param {string|string[]} [options.cc_email] - 참조 이메일. (문자열 또는 배열)
|
||||
* @param {string|string[]} [options.bcc_email] - 숨은 참조 이메일. (문자열 또는 배열)
|
||||
* @param {function} [options.onSuccess] - 발송 성공 시 실행될 콜백 함수
|
||||
* @param {function} [options.onError] - 발송 실패 시 실행될 콜백 함수
|
||||
* @param {function} [options.onComplete] - 성공/실패와 관계없이 마지막에 실행될 콜백 함수
|
||||
*/
|
||||
UniversalMailer.prototype.send = function(options) {
|
||||
const formData = new FormData();
|
||||
formData.append('template_code', options.template_code);
|
||||
|
||||
// 수신자 처리 (배열 또는 문자열)
|
||||
if (options.to_email) {
|
||||
if (Array.isArray(options.to_email)) {
|
||||
options.to_email.forEach(email => formData.append('to_email[]', email));
|
||||
} else {
|
||||
formData.append('to_email', options.to_email);
|
||||
}
|
||||
}
|
||||
|
||||
// 참조자 처리 (배열 또는 문자열)
|
||||
if (options.cc_email) {
|
||||
if (Array.isArray(options.cc_email)) {
|
||||
options.cc_email.forEach(email => {
|
||||
if (email === 'ADMIN_EMAIL') {
|
||||
email = ADMIN_EMAIL;
|
||||
}
|
||||
formData.append('cc_email[]', email)
|
||||
});
|
||||
} else if ($cc_email === 'ADMIN_EMAIL') {
|
||||
formData.append('cc_email', ADMIN_EMAIL);
|
||||
} else {
|
||||
formData.append('cc_email', options.cc_email);
|
||||
}
|
||||
}
|
||||
|
||||
// 숨은 참조자 처리 (배열 또는 문자열)
|
||||
if (options.bcc_email) {
|
||||
if (Array.isArray(options.bcc_email)) {
|
||||
options.bcc_email.forEach(email => formData.append('bcc_email[]', email));
|
||||
} else {
|
||||
formData.append('bcc_email', options.bcc_email);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.variables) {
|
||||
for (const key in options.variables) {
|
||||
formData.append(`variables[${key}]`, options.variables[key]);
|
||||
}
|
||||
}
|
||||
|
||||
// GnuBoard의 g5_url 변수를 사용하여 AJAX 요청 URL을 생성합니다.
|
||||
const ajax_url = (typeof g5_url !== 'undefined' ? g5_url : '') + '/adm/mail_manage/ajax_universal_send.php';
|
||||
|
||||
fetch(ajax_url, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error('서버와의 통신에 실패했습니다.');
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
if (typeof options.onSuccess === 'function') options.onSuccess(data);
|
||||
} else {
|
||||
throw new Error(data.message || '알 수 없는 오류가 발생했습니다.');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
if (typeof options.onError === 'function') options.onError(error.message);
|
||||
})
|
||||
.finally(() => {
|
||||
if (typeof options.onComplete === 'function') options.onComplete();
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
|
||||
// mailer 객체를 window 전역 객체에 등록하여 어디서든 접근 가능하게 합니다.
|
||||
window.universalMailer = new UniversalMailer();
|
||||
|
||||
})(window);
|
||||
Reference in New Issue
Block a user