// Main JavaScript for SmartSolTech Website
document.addEventListener('DOMContentLoaded', function() {
// Initialize AOS (Animate On Scroll)
if (typeof AOS !== 'undefined') {
AOS.init({
duration: 800,
easing: 'ease-in-out',
once: true,
offset: 100
});
}
// Mobile Navigation Toggle
const mobileMenuButton = document.querySelector('.mobile-menu-button');
const mobileMenu = document.querySelector('.mobile-menu');
if (mobileMenuButton && mobileMenu) {
mobileMenuButton.addEventListener('click', function() {
mobileMenu.classList.toggle('show');
const isOpen = mobileMenu.classList.contains('show');
// Toggle button icon
const icon = mobileMenuButton.querySelector('svg');
if (icon) {
icon.innerHTML = isOpen
? ''
: '';
}
// Accessibility
mobileMenuButton.setAttribute('aria-expanded', isOpen);
});
// Close mobile menu when clicking outside
document.addEventListener('click', function(e) {
if (!mobileMenuButton.contains(e.target) && !mobileMenu.contains(e.target)) {
mobileMenu.classList.remove('show');
mobileMenuButton.setAttribute('aria-expanded', 'false');
}
});
}
// Navbar Scroll Effect
const navbar = document.querySelector('nav');
let lastScrollY = window.scrollY;
window.addEventListener('scroll', function() {
const currentScrollY = window.scrollY;
if (navbar) {
if (currentScrollY > 100) {
navbar.classList.add('navbar-scrolled');
} else {
navbar.classList.remove('navbar-scrolled');
}
// Hide/show navbar on scroll
if (currentScrollY > lastScrollY && currentScrollY > 200) {
navbar.style.transform = 'translateY(-100%)';
} else {
navbar.style.transform = 'translateY(0)';
}
}
lastScrollY = currentScrollY;
});
// Smooth Scrolling for Anchor Links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function(e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
const offsetTop = target.offsetTop - (navbar ? navbar.offsetHeight : 0);
window.scrollTo({
top: offsetTop,
behavior: 'smooth'
});
}
});
});
// Contact Form Handler
const quickContactForm = document.getElementById('quick-contact-form');
if (quickContactForm) {
quickContactForm.addEventListener('submit', handleContactSubmit);
}
const mainContactForm = document.getElementById('contact-form');
if (mainContactForm) {
mainContactForm.addEventListener('submit', handleContactSubmit);
}
async function handleContactSubmit(e) {
e.preventDefault();
const form = e.target;
const submitButton = form.querySelector('button[type="submit"]');
const originalText = submitButton.textContent;
// Show loading state
submitButton.disabled = true;
submitButton.classList.add('btn-loading');
submitButton.textContent = '전송 중...';
try {
const formData = new FormData(form);
const data = Object.fromEntries(formData.entries());
const response = await fetch('/api/contact/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
const result = await response.json();
if (result.success) {
showNotification('메시지가 성공적으로 전송되었습니다! 곧 연락드리겠습니다.', 'success');
form.reset();
} else {
showNotification('메시지 전송 중 오류가 발생했습니다. 다시 시도해주세요.', 'error');
}
} catch (error) {
console.error('Contact form error:', error);
showNotification('메시지 전송 중 오류가 발생했습니다. 다시 시도해주세요.', 'error');
} finally {
// Reset button state
submitButton.disabled = false;
submitButton.classList.remove('btn-loading');
submitButton.textContent = originalText;
}
}
// Notification System
function showNotification(message, type = 'info') {
const notification = document.createElement('div');
notification.className = `notification notification-${type}`;
notification.innerHTML = `
이 웹사이트는 더 나은 서비스 제공을 위해 쿠키를 사용합니다.
`;
banner.style.cssText = `
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.9);
color: white;
padding: 1rem;
z-index: 9999;
transform: translateY(100%);
transition: transform 0.3s ease;
`;
document.body.appendChild(banner);
setTimeout(() => {
banner.style.transform = 'translateY(0)';
}, 100);
document.getElementById('accept-cookies').addEventListener('click', () => {
localStorage.setItem('cookieConsent', 'accepted');
banner.style.transform = 'translateY(100%)';
setTimeout(() => banner.remove(), 300);
});
document.getElementById('decline-cookies').addEventListener('click', () => {
localStorage.setItem('cookieConsent', 'declined');
banner.style.transform = 'translateY(100%)';
setTimeout(() => banner.remove(), 300);
});
}
// Initialize cookie consent
// initCookieConsent();
// Parallax Effect
const parallaxElements = document.querySelectorAll('.parallax');
function updateParallax() {
const scrollY = window.pageYOffset;
parallaxElements.forEach(element => {
const speed = element.dataset.speed || 0.5;
const yPos = -(scrollY * speed);
element.style.transform = `translateY(${yPos}px)`;
});
}
if (parallaxElements.length > 0) {
window.addEventListener('scroll', updateParallax);
}
// Counter Animation
const counters = document.querySelectorAll('.counter');
const counterObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
animateCounter(entry.target);
counterObserver.unobserve(entry.target);
}
});
});
counters.forEach(counter => counterObserver.observe(counter));
function animateCounter(element) {
const target = parseInt(element.dataset.count);
const duration = 2000;
const start = performance.now();
function updateCounter(currentTime) {
const elapsed = currentTime - start;
const progress = Math.min(elapsed / duration, 1);
const current = Math.floor(progress * target);
element.textContent = current.toLocaleString();
if (progress < 1) {
requestAnimationFrame(updateCounter);
}
}
requestAnimationFrame(updateCounter);
}
// Typing Effect for Hero Text
const typingElements = document.querySelectorAll('.typing-effect');
typingElements.forEach(element => {
const text = element.textContent;
element.textContent = '';
element.style.borderRight = '2px solid';
let i = 0;
const timer = setInterval(() => {
element.textContent += text[i];
i++;
if (i >= text.length) {
clearInterval(timer);
element.style.borderRight = 'none';
}
}, 100);
});
// Handle form validation
const forms = document.querySelectorAll('form[data-validate]');
forms.forEach(form => {
form.addEventListener('submit', function(e) {
if (!validateForm(this)) {
e.preventDefault();
}
});
// Real-time validation
const inputs = form.querySelectorAll('input, textarea');
inputs.forEach(input => {
input.addEventListener('blur', () => validateField(input));
input.addEventListener('input', () => clearFieldError(input));
});
});
function validateForm(form) {
let isValid = true;
const inputs = form.querySelectorAll('input[required], textarea[required]');
inputs.forEach(input => {
if (!validateField(input)) {
isValid = false;
}
});
return isValid;
}
function validateField(field) {
const value = field.value.trim();
const type = field.type;
let isValid = true;
let message = '';
// Required field check
if (field.hasAttribute('required') && !value) {
isValid = false;
message = '이 필드는 필수입니다.';
}
// Email validation
if (type === 'email' && value) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(value)) {
isValid = false;
message = '올바른 이메일 형식을 입력해주세요.';
}
}
// Phone validation
if (type === 'tel' && value) {
const phoneRegex = /^[0-9-+\s()]+$/;
if (!phoneRegex.test(value)) {
isValid = false;
message = '올바른 전화번호 형식을 입력해주세요.';
}
}
// Show/hide error
if (!isValid) {
showFieldError(field, message);
} else {
clearFieldError(field);
}
return isValid;
}
function showFieldError(field, message) {
clearFieldError(field);
field.classList.add('error');
const errorDiv = document.createElement('div');
errorDiv.className = 'field-error';
errorDiv.textContent = message;
errorDiv.style.cssText = 'color: #ef4444; font-size: 0.875rem; margin-top: 0.25rem;';
field.parentNode.appendChild(errorDiv);
}
function clearFieldError(field) {
field.classList.remove('error');
const errorDiv = field.parentNode.querySelector('.field-error');
if (errorDiv) {
errorDiv.remove();
}
}
});
// Utility Functions
const utils = {
// Debounce function
debounce: function(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
},
// Throttle function
throttle: function(func, limit) {
let inThrottle;
return function() {
const args = arguments;
const context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
},
// Format currency
formatCurrency: function(amount, currency = 'KRW') {
return new Intl.NumberFormat('ko-KR', {
style: 'currency',
currency: currency,
minimumFractionDigits: 0
}).format(amount);
},
// Format date
formatDate: function(date, options = {}) {
const defaultOptions = {
year: 'numeric',
month: 'long',
day: 'numeric'
};
return new Intl.DateTimeFormat('ko-KR', { ...defaultOptions, ...options }).format(new Date(date));
}
};
// Global error handler
window.addEventListener('error', function(e) {
console.error('Global error:', e.error);
// Could send error to analytics service
});
// Unhandled promise rejection handler
window.addEventListener('unhandledrejection', function(e) {
console.error('Unhandled promise rejection:', e.reason);
// Could send error to analytics service
});
// Export for use in other modules
if (typeof module !== 'undefined' && module.exports) {
module.exports = utils;
}