feat: implement contact form and Chatwoot widget
Add Resend API-based contact form and Chatwoot widget integration. Co-authored-by: ASOwnerYT <[email protected]>
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { name, email, message } = await request.json();
|
||||
|
||||
// Validate inputs
|
||||
if (!name || !email || !message) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Missing required fields' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Validate email format
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(email)) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Invalid email address' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const resendApiKey = process.env.RESEND_API_KEY;
|
||||
if (!resendApiKey) {
|
||||
console.error('[v0] RESEND_API_KEY not configured');
|
||||
return NextResponse.json(
|
||||
{ error: 'Email service not configured' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
|
||||
// Send email via Resend
|
||||
const response = await fetch('https://api.resend.com/emails', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${resendApiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
from: 'Cloudrite Contact Form <[email protected]>',
|
||||
to: '[email protected]',
|
||||
replyTo: email,
|
||||
subject: `New Contact Form Submission from ${name}`,
|
||||
html: `
|
||||
<h2>New Contact Form Submission</h2>
|
||||
<p><strong>Name:</strong> ${escapeHtml(name)}</p>
|
||||
<p><strong>Email:</strong> ${escapeHtml(email)}</p>
|
||||
<p><strong>Message:</strong></p>
|
||||
<p>${escapeHtml(message).replace(/\n/g, '<br>')}</p>
|
||||
`,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
console.error('[v0] Resend API error:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to send email' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
{ success: true, message: 'Email sent successfully' },
|
||||
{ status: 200 }
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('[v0] Email route error:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Internal server error' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHtml(text: string): string {
|
||||
const map: { [key: string]: string } = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": ''',
|
||||
};
|
||||
return text.replace(/[&<>"']/g, (char) => map[char]);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Metadata } from 'next'
|
||||
import { Space_Grotesk, Inter } from 'next/font/google'
|
||||
import { Analytics } from '@vercel/analytics/next'
|
||||
import { ChatwootWidget } from '@/components/chatwoot'
|
||||
import './globals.css'
|
||||
|
||||
const spaceGrotesk = Space_Grotesk({
|
||||
@@ -45,6 +46,7 @@ export default function RootLayout({
|
||||
<body className={`${spaceGrotesk.variable} ${inter.variable} font-sans antialiased`}>
|
||||
{children}
|
||||
<Analytics />
|
||||
<ChatwootWidget />
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user