EMAIL_TEMPLATE/static/js/main.js
Patsy dec4fbe68d
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
Phase 1 done
2026-08-07 16:46:44 +02:00

319 lines
8.7 KiB
JavaScript

/**
* Café Bach Email Generator - Form Handling & Preview Logic
*/
document.addEventListener('DOMContentLoaded', function() {
const form = document.getElementById('emailForm');
const previewBtn = document.getElementById('previewBtn');
const descCount = document.getElementById('desc-count');
const descriptionField = document.getElementById('description');
// Character counter for description field
if (descriptionField && descCount) {
descriptionField.addEventListener('input', function() {
const currentLength = this.value.length;
const maxLength = this.maxLength;
descCount.textContent = `${currentLength}/${maxLength}`;
if (currentLength > maxLength * 0.9) {
descCount.style.color = '#e74c3c';
} else {
descCount.style.color = '#95a5a6';
}
});
}
// Form submission handler
if (form) {
form.addEventListener('submit', async function(e) {
e.preventDefault();
if (validateForm()) {
const formData = getFormData();
await submitFormData(formData);
}
});
}
// Preview button handler
if (previewBtn) {
previewBtn.addEventListener('click', function() {
if (validateForm()) {
const formData = getFormData();
showPreview(formData);
}
});
}
// Clear form handler
const clearBtn = document.querySelector('button[type="reset"]');
if (clearBtn) {
clearBtn.addEventListener('click', function() {
resetForm();
});
}
// Real-time validation on blur
const inputs = form ? form.querySelectorAll('input, textarea') : [];
inputs.forEach(input => {
input.addEventListener('blur', function() {
validateField(this);
});
});
// Real-time validation on input
inputs.forEach(input => {
if (input.required) {
input.addEventListener('input', function() {
if (this.classList.contains('error')) {
validateField(this);
}
});
}
});
});
/**
* Validate the entire form
*/
function validateForm() {
const form = document.getElementById('emailForm');
const inputs = form.querySelectorAll('input[required], textarea[required]');
let isValid = true;
inputs.forEach(input => {
if (!validateField(input)) {
isValid = false;
}
});
// Validate URLs if provided
const urlFields = ['image_url', 'button_url', 'next_event_url'];
urlFields.forEach(fieldName => {
const field = document.getElementById(fieldName);
if (field && field.value && !isValidUrl(field.value)) {
showError(fieldName, 'Please enter a valid URL');
isValid = false;
}
});
return isValid;
}
/**
* Validate a single field
*/
function validateField(field) {
const fieldName = field.id;
const value = field.value.trim();
// Clear previous error
clearError(fieldName);
// Required field check
if (field.required && !value) {
showError(fieldName, 'This field is required');
field.classList.add('error');
return false;
}
// Length validation
if (value && field.maxLength) {
if (value.length > field.maxLength) {
showError(fieldName, `Must be less than ${field.maxLength} characters`);
field.classList.add('error');
return false;
}
}
field.classList.remove('error');
return true;
}
/**
* Show error message for a field
*/
function showError(fieldName, message) {
const errorElement = document.getElementById(`${fieldName}-error`);
if (errorElement) {
errorElement.textContent = message;
}
}
/**
* Clear error message for a field
*/
function clearError(fieldName) {
const errorElement = document.getElementById(`${fieldName}-error`);
if (errorElement) {
errorElement.textContent = '';
}
}
/**
* Validate URL format
*/
function isValidUrl(string) {
try {
new URL(string);
return true;
} catch (_) {
return false;
}
}
/**
* Get form data as object
*/
function getFormData() {
const form = document.getElementById('emailForm');
const formData = new FormData(form);
const data = {};
for (let [key, value] of formData.entries()) {
data[key] = value;
}
// Convert to JSON string for display
data.jsonString = JSON.stringify(data, null, 2);
return data;
}
/**
* Submit form data to backend
*/
async function submitFormData(data) {
try {
const response = await fetch('/generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
if (response.ok) {
const result = await response.json();
console.log('Form submitted successfully:', result);
alert('Email generated successfully!');
} else {
console.error('Submission failed');
alert('Failed to generate email. Please try again.');
}
} catch (error) {
console.error('Submission error:', error);
alert('Network error. Please check your connection and try again.');
}
}
/**
* Show preview of email
*/
function showPreview(data) {
const previewContainer = document.getElementById('emailPreview');
// Format date for display
let formattedDate = '';
if (data.date_time) {
const date = new Date(data.date_time);
formattedDate = date.toLocaleString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
}
// Build HTML preview
const previewHTML = `
<div class="email-template">
<h1>${escapeHtml(data.title)}</h1>
${data.image_url ? `<img src="${escapeHtml(data.image_url)}" alt="Event Image" class="event-image">` : ''}
<div class="event-details">
<strong>📅 Date & Time</strong>
<p>${formattedDate || escapeHtml(data.date_time)}</p>
<strong>📍 Location</strong>
<p>${escapeHtml(data.location)}</p>
<strong>📝 Description</strong>
<p>${escapeHtml(data.description)}</p>
</div>
<a href="${escapeHtml(data.button_url)}" class="cta-button">${escapeHtml(data.button_text)}</a>
${data.next_event_text ? `
<div class="next-event">
<p><strong>Next Event:</strong> ${escapeHtml(data.next_event_text)}</p>
${data.next_event_url ? `<a href="${escapeHtml(data.next_event_url)}">Learn More</a>` : ''}
</div>
` : ''}
</div>
`;
previewContainer.innerHTML = previewHTML;
// Scroll to preview
previewContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
/**
* Reset form to initial state
*/
function resetForm() {
const form = document.getElementById('emailForm');
form.reset();
// Clear all error messages
const errorMessages = document.querySelectorAll('.error-message');
errorMessages.forEach(el => el.textContent = '');
// Remove error classes
const inputs = form.querySelectorAll('input, textarea');
inputs.forEach(input => input.classList.remove('error'));
// Reset character counter
const descCount = document.getElementById('desc-count');
if (descCount) {
descCount.textContent = '0/2000';
descCount.style.color = '#95a5a6';
}
}
/**
* Escape HTML to prevent XSS
*/
function escapeHtml(text) {
if (!text) return '';
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
/**
* Copy to clipboard functionality
*/
function copyToClipboard() {
const previewContent = document.getElementById('emailPreview').innerHTML;
navigator.clipboard.writeText(previewContent).then(() => {
const btn = document.getElementById('copyBtn');
if (btn) {
btn.textContent = '✓ Copied to Clipboard!';
setTimeout(() => {
btn.textContent = 'Copy HTML to Clipboard';
}, 2000);
}
}).catch(err => {
console.error('Failed to copy:', err);
alert('Failed to copy to clipboard. Please select and copy manually.');
});
}
// Export functions for global access
window.copyToClipboard = copyToClipboard;