feat: add contact message email template and localization support
- Introduced a new email template for contact messages, including localization for English and Chinese. - Updated the send-message action to utilize the new contact message template and added locale support. - Enhanced the contact form validation schema and improved code formatting for consistency.
This commit is contained in:
parent
345528e4b3
commit
a2849a9758
@ -532,6 +532,12 @@
|
||||
"subscribeNewsletter": {
|
||||
"body": "Thank you for subscribing to the newsletter. We will keep you updated with the latest news and updates.",
|
||||
"subject": "Thanks for subscribing"
|
||||
},
|
||||
"contactMessage": {
|
||||
"name": "Name: {name}",
|
||||
"email": "Email: {email}",
|
||||
"message": "Message: {message}",
|
||||
"subject": "Contact Message from Website"
|
||||
}
|
||||
}
|
||||
}
|
@ -531,6 +531,12 @@
|
||||
"subscribeNewsletter": {
|
||||
"body": "感谢您订阅我们的邮件列表,我们将为您提供最新的新闻和更新。",
|
||||
"subject": "感谢您的订阅"
|
||||
},
|
||||
"contactMessage": {
|
||||
"name": "姓名: {name}",
|
||||
"email": "邮箱: {email}",
|
||||
"message": "消息: {message}",
|
||||
"subject": "来自网站的联系消息"
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
import { websiteConfig } from '@/config';
|
||||
import { send } from '@/mail';
|
||||
import { getLocale } from 'next-intl/server';
|
||||
import { createSafeActionClient } from 'next-safe-action';
|
||||
import { z } from 'zod';
|
||||
|
||||
@ -9,20 +10,17 @@ import { z } from 'zod';
|
||||
const actionClient = createSafeActionClient();
|
||||
|
||||
/**
|
||||
* TODO: When using Zod for validation, how can I localize error messages?
|
||||
* DOC: When using Zod for validation, how can I localize error messages?
|
||||
* https://next-intl.dev/docs/environments/actions-metadata-route-handlers#server-actions
|
||||
*/
|
||||
// Contact form schema for validation
|
||||
const contactFormSchema = z.object({
|
||||
name: z
|
||||
.string()
|
||||
name: z.string()
|
||||
.min(3, { message: 'Name must be at least 3 characters' })
|
||||
.max(30, { message: 'Name must not exceed 30 characters' }),
|
||||
email: z
|
||||
.string()
|
||||
email: z.string()
|
||||
.email({ message: 'Please enter a valid email address' }),
|
||||
message: z
|
||||
.string()
|
||||
message: z.string()
|
||||
.min(10, { message: 'Message must be at least 10 characters' })
|
||||
.max(500, { message: 'Message must not exceed 500 characters' }),
|
||||
});
|
||||
@ -40,20 +38,18 @@ export const sendMessageAction = actionClient
|
||||
throw new Error('The mail receiver is not set');
|
||||
}
|
||||
|
||||
// Send email using the mail service
|
||||
// Customize the email template for your needs
|
||||
// TODO: add locale to the email or customize it by yourself?
|
||||
const locale = await getLocale();
|
||||
|
||||
// Send message as an email to admin
|
||||
const result = await send({
|
||||
to: websiteConfig.mail.to,
|
||||
subject: `Contact Form: Message from ${name}`,
|
||||
text: '',
|
||||
html: `
|
||||
<p><strong>Name:</strong> ${name}</p>
|
||||
<p><strong>Email:</strong> ${email}</p>
|
||||
<br />
|
||||
<p><strong>Message:</strong></p>
|
||||
<p>${message.replace(/\n/g, '<br />')}</p>
|
||||
`,
|
||||
template: 'contactMessage',
|
||||
context: {
|
||||
name,
|
||||
email,
|
||||
message,
|
||||
},
|
||||
locale,
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
|
34
src/mail/emails/contact-message.tsx
Normal file
34
src/mail/emails/contact-message.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import { defaultMessages } from '@/i18n/messages';
|
||||
import { routing } from '@/i18n/routing';
|
||||
import EmailLayout from '@/mail/components/email-layout';
|
||||
import type { BaseEmailProps } from '@/mail/types';
|
||||
import { Text } from '@react-email/components';
|
||||
import { createTranslator } from 'use-intl/core';
|
||||
|
||||
interface ContactMessageProps extends BaseEmailProps {
|
||||
name: string;
|
||||
email: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export function ContactMessage({ name, email, message, locale, messages }: ContactMessageProps) {
|
||||
const t = createTranslator({ locale, messages, });
|
||||
|
||||
return (
|
||||
<EmailLayout locale={locale} messages={messages}>
|
||||
<Text>{t('Mail.contactMessage.name', { name })}</Text>
|
||||
<Text>{t('Mail.contactMessage.email', { email })}</Text>
|
||||
<Text>{t('Mail.contactMessage.message', { message })}</Text>
|
||||
</EmailLayout>
|
||||
);
|
||||
}
|
||||
|
||||
ContactMessage.PreviewProps = {
|
||||
locale: routing.defaultLocale,
|
||||
messages: defaultMessages,
|
||||
name: 'username',
|
||||
email: 'username@example.com',
|
||||
message: 'This is a test message',
|
||||
};
|
||||
|
||||
export default ContactMessage;
|
@ -11,23 +11,13 @@ interface ForgotPasswordProps extends BaseEmailProps {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export function ForgotPassword({
|
||||
url,
|
||||
name,
|
||||
locale,
|
||||
messages,
|
||||
}: ForgotPasswordProps) {
|
||||
const t = createTranslator({
|
||||
locale,
|
||||
messages,
|
||||
});
|
||||
export function ForgotPassword({ url, name, locale, messages, }: ForgotPasswordProps) {
|
||||
const t = createTranslator({ locale, messages, });
|
||||
|
||||
return (
|
||||
<EmailLayout locale={locale} messages={messages}>
|
||||
<Text>{t('Mail.forgotPassword.title', { name })}</Text>
|
||||
|
||||
<Text>{t('Mail.forgotPassword.body')}</Text>
|
||||
|
||||
<EmailButton href={url}>
|
||||
{t('Mail.forgotPassword.resetPassword')}
|
||||
</EmailButton>
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { VerifyEmail } from './verify-email';
|
||||
import { ForgotPassword } from './forgot-password';
|
||||
import { SubscribeNewsletter } from './subscribe-newsletter';
|
||||
import { ContactMessage } from './contact-message';
|
||||
|
||||
/**
|
||||
* list all the email templates here
|
||||
@ -9,4 +10,5 @@ export const EmailTemplates = {
|
||||
forgotPassword: ForgotPassword,
|
||||
verifyEmail: VerifyEmail,
|
||||
subscribeNewsletter: SubscribeNewsletter,
|
||||
contactMessage: ContactMessage,
|
||||
} as const;
|
||||
|
@ -9,10 +9,7 @@ interface SubscribeNewsletterProps extends BaseEmailProps {
|
||||
}
|
||||
|
||||
export function SubscribeNewsletter({ locale, messages }: SubscribeNewsletterProps) {
|
||||
const t = createTranslator({
|
||||
locale,
|
||||
messages,
|
||||
});
|
||||
const t = createTranslator({ locale, messages, });
|
||||
|
||||
return (
|
||||
<EmailLayout locale={locale} messages={messages}>
|
||||
|
@ -12,17 +12,12 @@ interface VerifyEmailProps extends BaseEmailProps {
|
||||
}
|
||||
|
||||
export function VerifyEmail({ url, name, locale, messages }: VerifyEmailProps) {
|
||||
const t = createTranslator({
|
||||
locale,
|
||||
messages,
|
||||
});
|
||||
const t = createTranslator({ locale, messages, });
|
||||
|
||||
return (
|
||||
<EmailLayout locale={locale} messages={messages}>
|
||||
<Text>{t('Mail.verifyEmail.title', { name })}</Text>
|
||||
|
||||
<Text>{t('Mail.verifyEmail.body')}</Text>
|
||||
|
||||
<EmailButton href={url}>{t('Mail.verifyEmail.confirmEmail')}</EmailButton>
|
||||
</EmailLayout>
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user