Add spam review functionality to contact form and update translations
- Introduced a new spamReview property in the Form interface to handle manual review requests for messages flagged as spam. - Updated the Form component to display a spam warning and a manual review form with dynamic labels and placeholders based on translations. - Enhanced the Tailwind CSS styles for responsive subtitles across various components. - Added corresponding translations for the spam review feature in English, Dutch, German, and French, ensuring consistency and clarity in messaging. - Updated various components to integrate the new spamReview functionality, improving user experience and interaction.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
import type { Form as Props } from '~/types';
|
||||
import Button from '~/components/ui/Button.astro';
|
||||
|
||||
const { inputs, textarea, disclaimer, button = 'Contact us', description = '' } = Astro.props;
|
||||
const { inputs, textarea, disclaimer, button = 'Contact us', description = '', spamReview } = Astro.props;
|
||||
---
|
||||
|
||||
<style>
|
||||
@@ -15,14 +15,7 @@ const { inputs, textarea, disclaimer, button = 'Contact us', description = '' }
|
||||
}
|
||||
</style>
|
||||
|
||||
<form
|
||||
id="contact-form"
|
||||
name="contact"
|
||||
method="POST"
|
||||
action="/api/contact"
|
||||
class="needs-validation"
|
||||
novalidate
|
||||
>
|
||||
<form id="contact-form" name="contact" method="POST" action="/api/contact" class="needs-validation" novalidate>
|
||||
<!-- Form status messages -->
|
||||
<div id="form-success" class="hidden mb-6 p-4 bg-green-100 border border-green-200 text-green-700 rounded-lg">
|
||||
Your message has been sent successfully. We will get back to you soon!
|
||||
@@ -132,23 +125,51 @@ const { inputs, textarea, disclaimer, button = 'Contact us', description = '' }
|
||||
</form>
|
||||
|
||||
<!-- Manual Review UI -->
|
||||
<div id="spam-warning" style="display:none;" class="max-w-xl mx-auto rounded-lg backdrop-blur-sm bg-white/15 dark:bg-slate-900 border border-gray-200 dark:border-gray-700 shadow-md p-4 sm:p-6 lg:p-8 w-full mb-6">
|
||||
<div
|
||||
id="spam-warning"
|
||||
style="display:none;"
|
||||
class="max-w-xl mx-auto rounded-lg backdrop-blur-sm bg-white/15 dark:bg-slate-900 border border-gray-200 dark:border-gray-700 shadow-md p-4 sm:p-6 lg:p-8 w-full mb-6"
|
||||
>
|
||||
<p class="mb-4 text-lg font-medium text-gray-800 dark:text-gray-100">
|
||||
Your message was detected as spam and was not sent.<br>
|
||||
<span class="text-base font-normal text-gray-600 dark:text-gray-300">If you believe this is a mistake, you can request a manual review.</span>
|
||||
{spamReview?.title}<br />
|
||||
<span class="text-base font-normal text-gray-600 dark:text-gray-300">{spamReview?.description}</span>
|
||||
</p>
|
||||
<form id="manual-review-form" class="space-y-4">
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1" for="manual-email">Please re-enter your email address for confirmation</label>
|
||||
<input type="email" id="manual-email" required placeholder="Enter your email address again" class="py-3 px-4 block w-full text-md rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-slate-900" />
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1" for="manual-justification">Why is this not spam? <span class="text-gray-400">(optional)</span></label>
|
||||
<textarea id="manual-justification" placeholder="Explain why your message is legitimate..." class="py-3 px-4 block w-full text-md rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-slate-900"></textarea>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1" for="manual-email"
|
||||
>{spamReview?.emailLabel}</label
|
||||
>
|
||||
<input
|
||||
type="email"
|
||||
id="manual-email"
|
||||
required
|
||||
placeholder={spamReview?.emailPlaceholder}
|
||||
class="py-3 px-4 block w-full text-md rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-slate-900"
|
||||
/>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1" for="manual-justification"
|
||||
>{spamReview?.justificationLabel} <span class="text-gray-400">{spamReview?.justificationOptional}</span></label
|
||||
>
|
||||
<textarea
|
||||
id="manual-justification"
|
||||
placeholder={spamReview?.justificationPlaceholder}
|
||||
class="py-3 px-4 block w-full text-md rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-slate-900"
|
||||
></textarea>
|
||||
<input type="hidden" id="manual-token" />
|
||||
<button type="submit" class="mt-2 w-full py-3 px-4 rounded-lg bg-blue-600 hover:bg-blue-700 text-white font-semibold transition-colors">Request Manual Review</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="mt-2 w-full py-3 px-4 rounded-lg bg-blue-600 hover:bg-blue-700 text-white font-semibold transition-colors"
|
||||
>{spamReview?.button}</button
|
||||
>
|
||||
</form>
|
||||
<div id="manual-review-result" class="mt-4 text-center text-green-700 dark:text-green-400 font-medium"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// TypeScript: declare the property on window
|
||||
declare global {
|
||||
interface Window {
|
||||
__originalEmail?: string;
|
||||
}
|
||||
}
|
||||
async function setCsrfToken() {
|
||||
try {
|
||||
const res = await fetch('/api/contact?csrf=true');
|
||||
@@ -156,7 +177,7 @@ const { inputs, textarea, disclaimer, button = 'Contact us', description = '' }
|
||||
const data = await res.json();
|
||||
const csrfInput = document.getElementById('csrf_token');
|
||||
if (csrfInput && data.csrfToken) {
|
||||
csrfInput.value = data.csrfToken;
|
||||
(csrfInput as HTMLInputElement).value = data.csrfToken;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -195,9 +216,11 @@ const { inputs, textarea, disclaimer, button = 'Contact us', description = '' }
|
||||
const spamWarning = document.getElementById('spam-warning');
|
||||
const manualEmail = document.getElementById('manual-email') as HTMLInputElement | null;
|
||||
const manualToken = document.getElementById('manual-token') as HTMLInputElement | null;
|
||||
// Store the original email in a variable (not in the input)
|
||||
window.__originalEmail = String(formData.get('email'));
|
||||
if (spamWarning && manualEmail && manualToken) {
|
||||
spamWarning.style.display = 'block';
|
||||
manualEmail.value = String(formData.get('email'));
|
||||
manualEmail.value = '';
|
||||
manualToken.value = result.token;
|
||||
}
|
||||
return;
|
||||
@@ -236,7 +259,11 @@ const { inputs, textarea, disclaimer, button = 'Contact us', description = '' }
|
||||
const email = manualEmail.value;
|
||||
const justification = manualJustification.value;
|
||||
const token = manualToken.value;
|
||||
|
||||
// Check if the entered email matches the original
|
||||
if (typeof window.__originalEmail !== 'undefined' && email !== window.__originalEmail) {
|
||||
resultDiv.textContent = 'Email addresses do not match.';
|
||||
return;
|
||||
}
|
||||
const res = await fetch('/api/contact/manual-review', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@@ -244,7 +271,8 @@ const { inputs, textarea, disclaimer, button = 'Contact us', description = '' }
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.success) {
|
||||
resultDiv.textContent = 'Your request for manual review has been submitted. Thank you!';
|
||||
resultDiv.textContent =
|
||||
spamReview?.resultSuccess || 'Your request for manual review has been submitted. Thank you!';
|
||||
// Hide spam-warning and show the normal form again after a short delay
|
||||
setTimeout(() => {
|
||||
if (spamWarning) spamWarning.style.display = 'none';
|
||||
@@ -255,7 +283,8 @@ const { inputs, textarea, disclaimer, button = 'Contact us', description = '' }
|
||||
resultDiv.textContent = '';
|
||||
}, 2000);
|
||||
} else {
|
||||
resultDiv.textContent = data.error || 'There was an error submitting your manual review request.';
|
||||
resultDiv.textContent =
|
||||
data.error || spamReview?.resultError || 'There was an error submitting your manual review request.';
|
||||
}
|
||||
};
|
||||
}
|
||||
|
Reference in New Issue
Block a user