fix: recaptcha badge
This commit is contained in:
1 parent
e24cf8d0be
commit
5f396f7c07
3 files changed
+121
-128
No files matched your search
+119
-118
@@ -253,132 +253,133 @@ export default async function Home() {
|
||||
<input type="email" name="email" id="form-email" placeholder="Your Email" required />
|
||||
<input type="text" name="subject" id="form-subject" placeholder="Subject" required />
|
||||
<textarea name="message" id="form-message" rows={5} placeholder="Message" required></textarea>
|
||||
<div id="recaptcha-container" style={{ display: 'flex', justifyContent: 'center', margin: '10px 0' }}></div>
|
||||
|
||||
|
||||
{/* reCAPTCHA v3 Enterprise — invisible */}
|
||||
<Script src={`https://www.google.com/recaptcha/enterprise.js?render=${recaptchaSiteKey}`} strategy="afterInteractive" />
|
||||
<Script id="recaptcha-inline-script" strategy="afterInteractive">
|
||||
{`
|
||||
(function() {
|
||||
function showToast(type, title, msg) {
|
||||
var container = document.getElementById('toast-container');
|
||||
var backdrop = document.getElementById('toast-backdrop');
|
||||
var box = document.getElementById('toast-box');
|
||||
var icon = document.getElementById('toast-icon');
|
||||
var titleEl = document.getElementById('toast-title');
|
||||
var msgEl = document.getElementById('toast-msg');
|
||||
var closeBtn = document.getElementById('toast-close');
|
||||
|
||||
box.className = 'toast toast-' + type;
|
||||
icon.textContent = type === 'success' ? '✓' : '✕';
|
||||
titleEl.textContent = title;
|
||||
msgEl.textContent = msg || '';
|
||||
|
||||
container.classList.add('toast-visible');
|
||||
backdrop.classList.add('toast-visible');
|
||||
|
||||
var autoClose;
|
||||
function dismiss() {
|
||||
container.classList.remove('toast-visible');
|
||||
backdrop.classList.remove('toast-visible');
|
||||
clearTimeout(autoClose);
|
||||
}
|
||||
|
||||
closeBtn.onclick = dismiss;
|
||||
backdrop.onclick = dismiss;
|
||||
autoClose = setTimeout(dismiss, type === 'success' ? 4000 : 6000);
|
||||
}
|
||||
|
||||
// Check URL params for status on load
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Move recaptcha badge inline
|
||||
var moveBadge = setInterval(function() {
|
||||
var badge = document.querySelector('.grecaptcha-badge');
|
||||
var container = document.getElementById('recaptcha-container');
|
||||
if (badge && container) {
|
||||
container.appendChild(badge);
|
||||
clearInterval(moveBadge);
|
||||
}
|
||||
}, 500);
|
||||
|
||||
var params = new URLSearchParams(window.location.search);
|
||||
var status = params.get('status');
|
||||
var msg = params.get('msg');
|
||||
if (status === 'success') {
|
||||
showToast('success', 'Message Sent!', 'Thank you for reaching out. I will get back to you soon.');
|
||||
history.replaceState(null, '', window.location.pathname + window.location.hash);
|
||||
} else if (status === 'error') {
|
||||
showToast('error', 'Something went wrong', msg || 'Please try again later.');
|
||||
history.replaceState(null, '', window.location.pathname + window.location.hash);
|
||||
}
|
||||
|
||||
var form = document.getElementById('contact-form');
|
||||
if (!form) return;
|
||||
|
||||
form.addEventListener('submit', function(event) {
|
||||
event.preventDefault();
|
||||
var btn = document.getElementById('btn-send-message');
|
||||
var originalText = btn.innerText;
|
||||
btn.innerText = 'Verifying...';
|
||||
btn.disabled = true;
|
||||
|
||||
grecaptcha.enterprise.ready(async function() {
|
||||
try {
|
||||
const token = await grecaptcha.enterprise.execute('${recaptchaSiteKey}', {action: 'submit'});
|
||||
|
||||
const verifyRes = await fetch('/api/contact', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ token: token })
|
||||
});
|
||||
|
||||
const verifyData = await verifyRes.json();
|
||||
if (!verifyData.success) {
|
||||
btn.innerText = originalText;
|
||||
btn.disabled = false;
|
||||
showToast('error', 'Verification Failed', verifyData.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
btn.innerText = 'Sending...';
|
||||
const formData = new FormData(form);
|
||||
formData.append('access_key', '${process.env.WEB3FORMS_ACCESS_KEY}');
|
||||
formData.append('from_name', 'Portfolio Contact Form');
|
||||
|
||||
const web3Res = await fetch('https://api.web3forms.com/submit', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
const web3Data = await web3Res.json();
|
||||
btn.innerText = originalText;
|
||||
btn.disabled = false;
|
||||
|
||||
if (web3Data.success) {
|
||||
form.reset();
|
||||
showToast('success', 'Message Sent!', 'Thank you for reaching out. I will get back to you soon.');
|
||||
} else {
|
||||
showToast('error', 'Something went wrong', 'The email service rejected the request. Please try again.');
|
||||
}
|
||||
|
||||
} catch(err) {
|
||||
btn.innerText = originalText;
|
||||
btn.disabled = false;
|
||||
showToast('error', 'Unexpected Error', 'reCAPTCHA failed to load. Please refresh and try again.');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
`}
|
||||
</Script>
|
||||
|
||||
<button type="submit" className="btn primary full-width" id="btn-send-message">Send Message</button>
|
||||
</form>
|
||||
<div id="recaptcha-container" style={{ display: 'flex', justifyContent: 'center', marginTop: '20px' }}></div>
|
||||
</div>
|
||||
</section>
|
||||
</ScrollReveal>
|
||||
|
||||
{/* reCAPTCHA v3 Enterprise — invisible */}
|
||||
<Script src={`https://www.google.com/recaptcha/enterprise.js?render=explicit&onload=onloadRecaptcha`} strategy="afterInteractive" />
|
||||
<Script id="recaptcha-inline-script" strategy="afterInteractive">
|
||||
{`
|
||||
(function() {
|
||||
function showToast(type, title, msg) {
|
||||
var container = document.getElementById('toast-container');
|
||||
var backdrop = document.getElementById('toast-backdrop');
|
||||
var box = document.getElementById('toast-box');
|
||||
var icon = document.getElementById('toast-icon');
|
||||
var titleEl = document.getElementById('toast-title');
|
||||
var msgEl = document.getElementById('toast-msg');
|
||||
var closeBtn = document.getElementById('toast-close');
|
||||
|
||||
box.className = 'toast toast-' + type;
|
||||
icon.textContent = type === 'success' ? '✓' : '✕';
|
||||
titleEl.textContent = title;
|
||||
msgEl.textContent = msg || '';
|
||||
|
||||
container.classList.add('toast-visible');
|
||||
backdrop.classList.add('toast-visible');
|
||||
|
||||
var autoClose;
|
||||
function dismiss() {
|
||||
container.classList.remove('toast-visible');
|
||||
backdrop.classList.remove('toast-visible');
|
||||
clearTimeout(autoClose);
|
||||
}
|
||||
|
||||
closeBtn.onclick = dismiss;
|
||||
backdrop.onclick = dismiss;
|
||||
autoClose = setTimeout(dismiss, type === 'success' ? 4000 : 6000);
|
||||
}
|
||||
|
||||
// Check URL params for status on load
|
||||
window.onloadRecaptcha = function() {
|
||||
grecaptcha.enterprise.ready(function() {
|
||||
window.recaptchaWidgetId = grecaptcha.enterprise.render('recaptcha-container', {
|
||||
sitekey: '${recaptchaSiteKey}',
|
||||
badge: 'inline',
|
||||
size: 'invisible'
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var params = new URLSearchParams(window.location.search);
|
||||
var status = params.get('status');
|
||||
var msg = params.get('msg');
|
||||
if (status === 'success') {
|
||||
showToast('success', 'Message Sent!', 'Thank you for reaching out. I will get back to you soon.');
|
||||
history.replaceState(null, '', window.location.pathname + window.location.hash);
|
||||
} else if (status === 'error') {
|
||||
showToast('error', 'Something went wrong', msg || 'Please try again later.');
|
||||
history.replaceState(null, '', window.location.pathname + window.location.hash);
|
||||
}
|
||||
|
||||
var form = document.getElementById('contact-form');
|
||||
if (!form) return;
|
||||
|
||||
form.addEventListener('submit', function(event) {
|
||||
event.preventDefault();
|
||||
var btn = document.getElementById('btn-send-message');
|
||||
var originalText = btn.innerText;
|
||||
btn.innerText = 'Verifying...';
|
||||
btn.disabled = true;
|
||||
|
||||
grecaptcha.enterprise.ready(async function() {
|
||||
try {
|
||||
const token = await grecaptcha.enterprise.execute(window.recaptchaWidgetId, {action: 'submit'});
|
||||
|
||||
const verifyRes = await fetch('/api/contact', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ token: token })
|
||||
});
|
||||
|
||||
const verifyData = await verifyRes.json();
|
||||
if (!verifyData.success) {
|
||||
btn.innerText = originalText;
|
||||
btn.disabled = false;
|
||||
showToast('error', 'Verification Failed', verifyData.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
btn.innerText = 'Sending...';
|
||||
const formData = new FormData(form);
|
||||
formData.append('access_key', '${process.env.WEB3FORMS_ACCESS_KEY}');
|
||||
formData.append('from_name', 'Portfolio Contact Form');
|
||||
|
||||
const web3Res = await fetch('https://api.web3forms.com/submit', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
const web3Data = await web3Res.json();
|
||||
btn.innerText = originalText;
|
||||
btn.disabled = false;
|
||||
|
||||
if (web3Data.success) {
|
||||
form.reset();
|
||||
showToast('success', 'Message Sent!', 'Thank you for reaching out. I will get back to you soon.');
|
||||
} else {
|
||||
showToast('error', 'Something went wrong', 'The email service rejected the request. Please try again.');
|
||||
}
|
||||
|
||||
} catch(err) {
|
||||
btn.innerText = originalText;
|
||||
btn.disabled = false;
|
||||
showToast('error', 'Unexpected Error', 'reCAPTCHA failed to load. Please refresh and try again.');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
`}
|
||||
</Script>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -107,12 +107,4 @@
|
||||
.btn.full-width {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* --- Custom reCAPTCHA Badge Styling (Inline) --- */
|
||||
.grecaptcha-badge {
|
||||
position: static !important;
|
||||
margin-top: 15px !important;
|
||||
box-shadow: none !important;
|
||||
display: block !important;
|
||||
}
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
import { MetadataRoute } from 'next';
|
||||
|
||||
export default function sitemap(): MetadataRoute.Sitemap {
|
||||
// const baseUrl = 'https://crischarles.vercel.app';
|
||||
const baseUrl = 'http://localhost:3000';
|
||||
const baseUrl = 'https://crischarles.vercel.app';
|
||||
// const baseUrl = 'http://localhost:3000';
|
||||
const lastMod = '2026-06-11T07:44:07+00:00';
|
||||
|
||||
return [
|
||||
|
||||
Reference in new issue
Block a user