From d35903bf5b56cc40e3b61d34f57c192d6a8b3430 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Wed, 6 Aug 2025 19:35:38 -0400 Subject: Added contact page Prompt: Create a contact page --- static/contact-script.js | 399 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 399 insertions(+) create mode 100644 static/contact-script.js (limited to 'static/contact-script.js') diff --git a/static/contact-script.js b/static/contact-script.js new file mode 100644 index 0000000..d436dd5 --- /dev/null +++ b/static/contact-script.js @@ -0,0 +1,399 @@ +// DOM Elements +const contactForm = document.getElementById('contactForm'); +const submitBtn = document.getElementById('submitBtn'); +const toast = document.getElementById('toast'); +const formSuccess = document.getElementById('formSuccess'); +const formError = document.getElementById('formError'); +const messageTextarea = document.getElementById('message'); +const charCount = document.getElementById('charCount'); + +// Form validation +const validateEmail = (email) => { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(email); +}; + +const validatePhone = (phone) => { + const phoneRegex = /^[\+]?[1-9][\d]{0,15}$/; + return phone === '' || phoneRegex.test(phone.replace(/[\s\-\(\)]/g, '')); +}; + +const showError = (fieldId, message) => { + const errorElement = document.getElementById(fieldId + 'Error'); + const inputElement = document.getElementById(fieldId); + + if (errorElement) { + errorElement.textContent = message; + } + if (inputElement) { + inputElement.classList.add('error'); + } +}; + +const clearError = (fieldId) => { + const errorElement = document.getElementById(fieldId + 'Error'); + const inputElement = document.getElementById(fieldId); + + if (errorElement) { + errorElement.textContent = ''; + } + if (inputElement) { + inputElement.classList.remove('error'); + } +}; + +const clearAllErrors = () => { + const errorElements = document.querySelectorAll('.error-message'); + const inputElements = document.querySelectorAll('.error'); + + errorElements.forEach(el => el.textContent = ''); + inputElements.forEach(el => el.classList.remove('error')); +}; + +// Character counter for message textarea +messageTextarea.addEventListener('input', () => { + const length = messageTextarea.value.length; + charCount.textContent = length; + + if (length > 1000) { + charCount.style.color = 'var(--error)'; + messageTextarea.style.borderColor = 'var(--error)'; + } else { + charCount.style.color = 'var(--text-secondary)'; + messageTextarea.style.borderColor = 'var(--border)'; + } +}); + +// Real-time validation +document.getElementById('email').addEventListener('blur', (e) => { + const email = e.target.value.trim(); + if (email && !validateEmail(email)) { + showError('email', 'Please enter a valid email address'); + } else { + clearError('email'); + } +}); + +document.getElementById('phone').addEventListener('blur', (e) => { + const phone = e.target.value.trim(); + if (phone && !validatePhone(phone)) { + showError('phone', 'Please enter a valid phone number'); + } else { + clearError('phone'); + } +}); + +// Clear errors on input +['firstName', 'lastName', 'email', 'phone', 'subject', 'message'].forEach(fieldId => { + const element = document.getElementById(fieldId); + if (element) { + element.addEventListener('input', () => clearError(fieldId)); + } +}); + +// Set loading state +const setLoadingState = (loading) => { + if (loading) { + submitBtn.classList.add('loading'); + submitBtn.disabled = true; + } else { + submitBtn.classList.remove('loading'); + submitBtn.disabled = false; + } +}; + +// Show toast notification +const showToast = (message, type = 'success') => { + const toastMessage = toast.querySelector('.toast-message'); + const toastIcon = toast.querySelector('.toast-icon'); + + toastMessage.textContent = message; + + if (type === 'success') { + toast.style.background = 'var(--success)'; + toastIcon.textContent = '✓'; + } else { + toast.style.background = 'var(--error)'; + toastIcon.textContent = '✕'; + } + + toast.classList.add('show'); + + setTimeout(() => { + toast.classList.remove('show'); + }, 4000); +}; + +// API Configuration +const API_BASE_URL = 'https://api.taskflow.com/v1'; +const CONTACT_ENDPOINT = `${API_BASE_URL}/contact`; + +// Submit contact form +const submitContactForm = async (formData) => { + try { + const response = await fetch(CONTACT_ENDPOINT, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }, + body: JSON.stringify(formData) + }); + + const data = await response.json(); + + if (!response.ok) { + throw new Error(data.message || `HTTP error! status: ${response.status}`); + } + + return data; + } catch (error) { + if (error.name === 'TypeError' && error.message.includes('fetch')) { + throw new Error('Network error. Please check your connection and try again.'); + } + throw error; + } +}; + +// Form submission handler +contactForm.addEventListener('submit', async (e) => { + e.preventDefault(); + + // Clear previous states + clearAllErrors(); + formSuccess.classList.remove('show'); + formError.classList.remove('show'); + + // Get form data + const formData = new FormData(contactForm); + const data = { + firstName: formData.get('firstName').trim(), + lastName: formData.get('lastName').trim(), + email: formData.get('email').trim(), + phone: formData.get('phone').trim(), + company: formData.get('company').trim(), + subject: formData.get('subject'), + priority: formData.get('priority'), + message: formData.get('message').trim(), + newsletter: formData.get('newsletter') === 'on', + privacy: formData.get('privacy') === 'on' + }; + + // Validate required fields + let hasErrors = false; + + if (!data.firstName) { + showError('firstName', 'First name is required'); + hasErrors = true; + } + + if (!data.lastName) { + showError('lastName', 'Last name is required'); + hasErrors = true; + } + + if (!data.email) { + showError('email', 'Email address is required'); + hasErrors = true; + } else if (!validateEmail(data.email)) { + showError('email', 'Please enter a valid email address'); + hasErrors = true; + } + + if (data.phone && !validatePhone(data.phone)) { + showError('phone', 'Please enter a valid phone number'); + hasErrors = true; + } + + if (!data.subject) { + showError('subject', 'Please select a subject'); + hasErrors = true; + } + + if (!data.message) { + showError('message', 'Message is required'); + hasErrors = true; + } else if (data.message.length > 1000) { + showError('message', 'Message must be 1000 characters or less'); + hasErrors = true; + } + + if (!data.privacy) { + showError('privacy', 'You must agree to the Privacy Policy and Terms of Service'); + hasErrors = true; + } + + if (hasErrors) { + return; + } + + // Set loading state + setLoadingState(true); + + try { + // Submit form data + const response = await submitContactForm(data); + + // Show success message + formSuccess.classList.add('show'); + showToast('Message sent successfully! We\'ll get back to you soon.', 'success'); + + // Reset form + contactForm.reset(); + charCount.textContent = '0'; + + // Scroll to success message + formSuccess.scrollIntoView({ behavior: 'smooth', block: 'center' }); + + } catch (error) { + console.error('Contact form error:', error); + + // Show error message + const errorMessage = document.getElementById('errorMessage'); + errorMessage.textContent = error.message || 'Something went wrong. Please try again.'; + formError.classList.add('show'); + + showToast('Failed to send message. Please try again.', 'error'); + + // Scroll to error message + formError.scrollIntoView({ behavior: 'smooth', block: 'center' }); + + } finally { + setLoadingState(false); + } +}); + +// FAQ functionality +document.querySelectorAll('.faq-question').forEach(question => { + question.addEventListener('click', () => { + const faqId = question.dataset.faq; + const answer = document.getElementById(`faq-${faqId}`); + const isActive = question.classList.contains('active'); + + // Close all other FAQs + document.querySelectorAll('.faq-question').forEach(q => { + q.classList.remove('active'); + }); + document.querySelectorAll('.faq-answer').forEach(a => { + a.classList.remove('active'); + }); + + // Toggle current FAQ + if (!isActive) { + question.classList.add('active'); + answer.classList.add('active'); + } + }); +}); + +// Contact option handlers +document.getElementById('liveChatBtn').addEventListener('click', () => { + showToast('Opening live chat...', 'success'); + // In a real app, this would open a chat widget + setTimeout(() => { + alert('Live chat would open here.\n\nThis would integrate with a service like Intercom, Zendesk Chat, or similar.'); + }, 1000); +}); + +document.getElementById('scheduleDemoBtn').addEventListener('click', () => { + showToast('Opening demo scheduler...', 'success'); + // In a real app, this would open a calendar booking widget + setTimeout(() => { + alert('Demo scheduler would open here.\n\nThis would integrate with Calendly, Acuity Scheduling, or similar.'); + }, 1000); +}); + +// Social media link handlers +document.querySelectorAll('.social-icon').forEach(icon => { + icon.addEventListener('click', (e) => { + e.preventDefault(); + const platform = icon.getAttribute('aria-label'); + showToast(`Opening ${platform}...`, 'success'); + }); +}); + +// Navbar background on scroll +window.addEventListener('scroll', () => { + const navbar = document.querySelector('.navbar'); + if (window.scrollY > 50) { + navbar.style.background = 'rgba(255, 255, 255, 0.98)'; + } else { + navbar.style.background = 'rgba(255, 255, 255, 0.95)'; + } +}); + +// Intersection Observer for animations +const observerOptions = { + threshold: 0.1, + rootMargin: '0px 0px -50px 0px' +}; + +const observer = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + entry.target.style.opacity = '1'; + entry.target.style.transform = 'translateY(0)'; + } + }); +}, observerOptions); + +// Observe elements for animation +document.querySelectorAll('.contact-card, .contact-detail, .faq-item').forEach(el => { + el.style.opacity = '0'; + el.style.transform = 'translateY(30px)'; + el.style.transition = 'opacity 0.6s ease, transform 0.6s ease'; + observer.observe(el); +}); + +// Hero animation on load +window.addEventListener('load', () => { + const heroTitle = document.querySelector('.hero-title'); + const heroSubtitle = document.querySelector('.hero-subtitle'); + + [heroTitle, heroSubtitle].forEach((element, index) => { + if (element) { + element.style.opacity = '0'; + element.style.transform = 'translateY(30px)'; + element.style.transition = 'opacity 0.8s ease, transform 0.8s ease'; + + setTimeout(() => { + element.style.opacity = '1'; + element.style.transform = 'translateY(0)'; + }, 300 + (index * 200)); + } + }); +}); + +// Button click handlers +document.querySelectorAll('.btn').forEach(btn => { + btn.addEventListener('click', (e) => { + const buttonText = btn.textContent.toLowerCase(); + + if (buttonText.includes('trial') || buttonText.includes('start')) { + if (!btn.closest('form')) { // Don't interfere with form submission + e.preventDefault(); + showToast('Starting your free trial! Redirecting...', 'success'); + setTimeout(() => { + window.location.href = 'signup.html'; + }, 2000); + } + } + }); +}); + +// Auto-resize textarea +messageTextarea.addEventListener('input', function() { + this.style.height = 'auto'; + this.style.height = Math.min(this.scrollHeight, 200) + 'px'; +}); + +// Phone number formatting (optional enhancement) +document.getElementById('phone').addEventListener('input', function(e) { + let value = e.target.value.replace(/\D/g, ''); + if (value.length >= 6) { + value = value.replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3'); + } else if (value.length >= 3) { + value = value.replace(/(\d{3})(\d{0,3})/, '($1) $2'); + } + e.target.value = value; +}); -- cgit