Skip to content

Commit c9b1d57

Browse files
committed
Fix contact form submission issues
- Enhanced error handling in simple-contact-form.js - Added proper JSON/non-JSON response handling for Formspree - Enabled console logging in production (drop_console: false) - Added network timeout protection (30 seconds) - Improved error messages with specific details - Fixed script tag to use type='module' for proper Vite bundling - Added debug tools for testing form submissions The contact form now properly handles Formspree responses and provides better debugging information when issues occur.
1 parent 66ad4c1 commit c9b1d57

6 files changed

Lines changed: 269 additions & 6 deletions

File tree

contact.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ <h3>Thanks for your message!</h3>
140140
</div>
141141
</footer>
142142
<!-- Scripts loaded via main-page.js module -->
143-
<script src="/src/js/simple-contact-form.js"></script>
143+
<script type="module" src="/src/js/simple-contact-form.js"></script>
144144
<script>
145145
// Video banner handler
146146
window.addEventListener('load', function() {

debug-contact.html

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Debug Contact Form</title>
7+
<style>
8+
body {
9+
font-family: monospace;
10+
padding: 20px;
11+
background: #000;
12+
color: #0f0;
13+
}
14+
input, textarea, button {
15+
display: block;
16+
margin: 10px 0;
17+
padding: 10px;
18+
background: #000;
19+
color: #0f0;
20+
border: 1px solid #0f0;
21+
font-family: monospace;
22+
}
23+
button {
24+
cursor: pointer;
25+
background: #0f0;
26+
color: #000;
27+
}
28+
button:hover {
29+
background: #090;
30+
}
31+
#result {
32+
margin-top: 20px;
33+
padding: 20px;
34+
border: 1px solid #0f0;
35+
white-space: pre-wrap;
36+
max-height: 400px;
37+
overflow-y: auto;
38+
}
39+
.error { color: #f00; }
40+
.success { color: #0f0; }
41+
.info { color: #ff0; }
42+
</style>
43+
</head>
44+
<body>
45+
<h1>Contact Form Debugger</h1>
46+
<p>This will show you exactly what happens when submitting to Formspree</p>
47+
48+
<form id="debug-form">
49+
<input type="text" name="name" placeholder="Your Name" required>
50+
<input type="email" name="email" placeholder="your@email.com" required>
51+
<textarea name="message" placeholder="Your message" rows="5" required></textarea>
52+
<button type="submit">Submit to Formspree</button>
53+
</form>
54+
55+
<div id="result"></div>
56+
57+
<script>
58+
const form = document.getElementById('debug-form');
59+
const result = document.getElementById('result');
60+
61+
function log(message, type = 'info') {
62+
const timestamp = new Date().toLocaleTimeString();
63+
result.innerHTML += `<span class="${type}">[${timestamp}] ${message}</span>\n`;
64+
result.scrollTop = result.scrollHeight;
65+
}
66+
67+
form.addEventListener('submit', async (e) => {
68+
e.preventDefault();
69+
result.innerHTML = '';
70+
71+
log('Starting form submission...', 'info');
72+
73+
const formData = new FormData(form);
74+
log('Form data prepared:', 'info');
75+
for (let [key, value] of formData.entries()) {
76+
log(` ${key}: ${value}`, 'info');
77+
}
78+
79+
try {
80+
log('\nSending to: https://formspree.io/f/xeoeenqv', 'info');
81+
82+
const response = await fetch('https://formspree.io/f/xeoeenqv', {
83+
method: 'POST',
84+
body: formData,
85+
headers: {
86+
'Accept': 'application/json'
87+
}
88+
});
89+
90+
log(`\nResponse Status: ${response.status} ${response.statusText}`, response.ok ? 'success' : 'error');
91+
92+
// Log all headers
93+
log('\nResponse Headers:', 'info');
94+
for (let [key, value] of response.headers.entries()) {
95+
log(` ${key}: ${value}`, 'info');
96+
}
97+
98+
const contentType = response.headers.get('content-type');
99+
log(`\nContent-Type: ${contentType}`, 'info');
100+
101+
// Try to read response
102+
if (contentType && contentType.includes('application/json')) {
103+
try {
104+
const data = await response.json();
105+
log('\nJSON Response:', 'info');
106+
log(JSON.stringify(data, null, 2), response.ok ? 'success' : 'error');
107+
108+
if (data.error || data.errors) {
109+
log('\nERRORS FOUND IN RESPONSE!', 'error');
110+
}
111+
} catch (e) {
112+
log('\nFailed to parse JSON: ' + e.message, 'error');
113+
const text = await response.text();
114+
log('Raw response: ' + text, 'error');
115+
}
116+
} else {
117+
const text = await response.text();
118+
log('\nText Response:', 'info');
119+
log(text, response.ok ? 'success' : 'error');
120+
}
121+
122+
if (response.ok) {
123+
log('\n✓ Form submitted successfully!', 'success');
124+
log('Check your email and spam folder', 'success');
125+
log('Also check https://formspree.io dashboard', 'success');
126+
} else {
127+
log('\n✗ Form submission failed!', 'error');
128+
}
129+
130+
} catch (error) {
131+
log('\nNetwork Error: ' + error.message, 'error');
132+
log(error.stack, 'error');
133+
}
134+
});
135+
</script>
136+
</body>
137+
</html>

src/js/simple-contact-form.js

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@ document.addEventListener('DOMContentLoaded', function() {
44
const successMessage = document.getElementById('form-success');
55

66
if (!form) {
7+
console.warn('Contact form not found on page');
78
return;
89
}
910

11+
console.log('Contact form initialized successfully');
12+
1013
form.addEventListener('submit', async function(e) {
1114
e.preventDefault();
15+
console.log('Form submission started');
1216

1317
// Get form data
1418
const formData = new FormData(form);
@@ -41,31 +45,101 @@ document.addEventListener('DOMContentLoaded', function() {
4145
}
4246

4347
try {
48+
console.log('Sending form to:', form.action);
49+
50+
// Add timeout to prevent hanging
51+
const controller = new AbortController();
52+
const timeoutId = setTimeout(() => controller.abort(), 30000); // 30 second timeout
53+
4454
const response = await fetch(form.action, {
4555
method: 'POST',
4656
body: formData,
4757
headers: {
4858
'Accept': 'application/json'
49-
}
59+
},
60+
signal: controller.signal
5061
});
5162

63+
clearTimeout(timeoutId);
64+
console.log('Response received:', response.status, response.statusText);
65+
66+
// Check if response is ok first
5267
if (response.ok) {
68+
// Try to parse as JSON if possible
69+
const contentType = response.headers.get('content-type');
70+
let responseData;
71+
72+
if (contentType && contentType.includes('application/json')) {
73+
try {
74+
responseData = await response.json();
75+
console.log('Formspree response data:', responseData);
76+
77+
// Check if Formspree actually accepted it
78+
if (responseData.ok === false) {
79+
console.error('Formspree rejected submission:', responseData);
80+
throw new Error('Formspree rejected the submission');
81+
}
82+
} catch (jsonError) {
83+
console.error('Failed to parse JSON response:', jsonError);
84+
// Continue anyway - Formspree might return success without JSON
85+
}
86+
} else {
87+
console.warn('Non-JSON response from Formspree. Content-Type:', contentType);
88+
// Try to read as text
89+
const textResponse = await response.text();
90+
console.log('Text response:', textResponse);
91+
}
92+
93+
console.log('Form submission successful! Check your email.');
94+
5395
// Hide form and show success
5496
form.style.display = 'none';
5597
if (successMessage) {
5698
successMessage.style.display = 'block';
5799
} else {
58-
alert('Thank you! Your message has been sent.');
100+
alert('Thank you! Your message has been sent.\n\nNote: If you don\'t receive an email, check:\n1. Your spam folder\n2. Formspree dashboard\n3. That the form is activated');
59101
}
60102

61103
// Reset form for next use
62104
form.reset();
63105
} else {
64-
throw new Error('Failed to send');
106+
// Try to get error details
107+
let errorMessage = 'Failed to send message';
108+
const contentType = response.headers.get('content-type');
109+
110+
if (contentType && contentType.includes('application/json')) {
111+
try {
112+
const errorData = await response.json();
113+
if (errorData.error) {
114+
errorMessage = errorData.error;
115+
} else if (errorData.errors) {
116+
errorMessage = errorData.errors.join(', ');
117+
}
118+
} catch (jsonError) {
119+
console.error('Failed to parse error response:', jsonError);
120+
}
121+
}
122+
123+
console.error('Form submission failed:', response.status, errorMessage);
124+
throw new Error(errorMessage);
65125
}
66126

67127
} catch (error) {
68-
alert('Sorry, there was an error sending your message. Please try again.');
128+
console.error('Contact form error:', error);
129+
130+
let userMessage = 'Sorry, there was an error sending your message. ';
131+
132+
if (error.name === 'AbortError') {
133+
userMessage += 'The request timed out. Please check your internet connection and try again.';
134+
} else if (error.message.includes('Failed to fetch')) {
135+
userMessage += 'Unable to connect to the server. Please check your internet connection.';
136+
} else if (error.message) {
137+
userMessage += error.message;
138+
}
139+
140+
userMessage += '\n\nYou can also email directly to dev@thomasjbutler.me';
141+
142+
alert(userMessage);
69143
} finally {
70144
// Re-enable button
71145
if (submitButton) {

test-contact-form.html

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>Test Contact Form</title>
5+
</head>
6+
<body>
7+
<h1>Contact Form Test</h1>
8+
<button onclick="testForm()">Test Form Submission</button>
9+
<div id="result"></div>
10+
11+
<script>
12+
async function testForm() {
13+
const resultDiv = document.getElementById('result');
14+
resultDiv.innerHTML = 'Sending test...';
15+
16+
const formData = new FormData();
17+
formData.append('name', 'Direct Test');
18+
formData.append('email', 'test@test.com');
19+
formData.append('message', 'Direct test at ' + new Date().toISOString());
20+
21+
try {
22+
const response = await fetch('https://formspree.io/f/xeoeenqv', {
23+
method: 'POST',
24+
body: formData,
25+
headers: {
26+
'Accept': 'application/json'
27+
}
28+
});
29+
30+
const responseText = await response.text();
31+
console.log('Response status:', response.status);
32+
console.log('Response:', responseText);
33+
34+
if (response.ok) {
35+
resultDiv.innerHTML = '<h2 style="color: green">Success!</h2><p>Check your email and Formspree dashboard</p>';
36+
} else {
37+
resultDiv.innerHTML = '<h2 style="color: red">Failed</h2><pre>' + responseText + '</pre>';
38+
}
39+
} catch (error) {
40+
resultDiv.innerHTML = '<h2 style="color: red">Error</h2><pre>' + error.message + '</pre>';
41+
console.error('Error:', error);
42+
}
43+
}
44+
</script>
45+
</body>
46+
</html>

test-formspree.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/bin/bash
2+
echo "Testing Formspree endpoint..."
3+
curl -X POST https://formspree.io/f/xeoeenqv \
4+
-H "Accept: application/json" \
5+
-d "name=Curl Test&email=test@example.com&message=Test from curl at $(date)" \
6+
-v

vite.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export default defineConfig({
3030
minify: 'terser',
3131
terserOptions: {
3232
compress: {
33-
drop_console: true,
33+
drop_console: false, // Keep console logs for debugging
3434
drop_debugger: true
3535
}
3636
}

0 commit comments

Comments
 (0)