Email Notifications
Automated email system for approval and rejection notifications using Resend
Dirly uses Resend to send automated email notifications to tool submitters when their submissions are reviewed.
Configuration
Email notifications require two environment variables:
.env.local
RESEND_API_KEY=re_your_api_key_here
FROM_EMAIL=Dirly <noreply@yourdomain.com>Get Resend API Key
Sign up at resend.com and create an API key from your dashboard.
Configure Sender Email
Set FROM_EMAIL to use your verified domain. If not set, it defaults to Dirly <onboarding@resend.dev>.
Verify Domain (Production)
For production use, verify your domain in Resend to avoid emails being marked as spam.
If RESEND_API_KEY is not set, email sending will fail gracefully with a console warning, but the approval/rejection process will still complete.
Email Service Implementation
The core email service is in lib/email.ts:11-36:
export async function sendEmail(data: EmailData) {
if (!process.env.RESEND_API_KEY) {
console.warn('RESEND_API_KEY not set, email not sent');
return { success: false, error: 'RESEND_API_KEY not configured' };
}
try {
const result = await resend.emails.send({
from: process.env.FROM_EMAIL || 'Dirly <onboarding@resend.dev>',
to: data.to,
subject: data.subject,
html: data.html,
});
if (result.error) {
console.error('Resend API Error:', result.error);
return { success: false, error: result.error };
}
console.log('Email sent successfully:', result.data?.id);
return { success: true, data: result.data };
} catch (error) {
console.error('Failed to send email:', error);
return { success: false, error };
}
}Approval Emails
When a tool is approved, the submitter receives a celebratory email.
Email Template
Defined in lib/email.ts:69-100:
export function approvalEmail(toolName: string, toolUrl: string) {
return {
subject: `🎉 Your Tool is Now Live - ${toolName}`,
html: `
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;">
<h2 style="color: #22c55e; border-bottom: 2px solid #22c55e; padding-bottom: 10px;">
Congratulations! Your tool has been approved!
</h2>
<p style="color: #666; line-height: 1.6;">
Great news! <strong>${toolName}</strong> has been approved and is now live on Dirly.
</p>
<div style="text-align: center; margin: 30px 0;">
<a href="${toolUrl}"
style="background: #6366f1; color: white; padding: 12px 30px; text-decoration: none; border-radius: 6px; display: inline-block;">
View Your Tool
</a>
</div>
<p style="color: #666; line-height: 1.6;">
Share your tool with the community and start collecting upvotes. The more engagement your tool receives, the higher it will rank in our directory.
</p>
<div style="margin-top: 30px; padding: 15px; background: #f0fdf4; border-radius: 8px; border-left: 4px solid #22c55e;">
<p style="color: #166534; margin: 0; font-size: 14px;">
<strong>Tip:</strong> Share your tool on social media to get more visibility and upvotes!
</p>
</div>
</div>
`
};
}Approval Email Contents
- Subject:
🎉 Your Tool is Now Live - {Tool Name} - Green success header with congratulations message
- **Prominent CTA button **linking to the live tool page
- Engagement tips to encourage sharing
- Tip callout with green styling suggesting social media promotion
Sending Approval Emails
Emails are sent from app/admin/tools/[id]/page.tsx:38-54 after successful approval:
await approveTool({ toolId });
// Send approval email
try {
await fetch('/api/send-email', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'approval',
recipientUserId: tool.submittedBy,
preferenceKey: 'reviewStatusUpdates',
data: {
toolName: tool.name,
toolUrl: `${window.location.origin}/tools/${tool.slug}`
}
}),
});
} catch (emailError) {
console.error('Failed to send approval email:', emailError);
}Rejection Emails
When a tool is rejected, the submitter receives feedback on why.
Email Template
Defined in lib/email.ts:102-128:
export function rejectionEmail(toolName: string, reason: string) {
return {
subject: `Tool Submission Update - ${toolName}`,
html: `
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;">
<h2 style="color: #ef4444; border-bottom: 2px solid #ef4444; padding-bottom: 10px;">
Tool Submission Update
</h2>
<p style="color: #666; line-height: 1.6;">
Thank you for submitting <strong>${toolName}</strong> to Dirly. After review, we were unable to approve your submission at this time.
</p>
<div style="margin: 20px 0; padding: 15px; background: #fef2f2; border-radius: 8px; border-left: 4px solid #ef4444;">
<p style="color: #991b1b; margin: 0; font-size: 14px;">
<strong>Reason:</strong><br>
${reason}
</p>
</div>
<p style="color: #666; line-height: 1.6;">
You're welcome to resubmit after addressing the feedback above. If you have any questions, feel free to reach out to our support team.
</p>
</div>
`
};
}Rejection Email Contents
- Subject:
Tool Submission Update - {Tool Name} - Red header indicating rejection
- Polite rejection message thanking them for submitting
- Prominent reason callout with red styling showing admin feedback
- Invitation to resubmit after addressing issues
- Support contact offer for questions
Sending Rejection Emails
Emails are sent from app/admin/tools/[id]/page.tsx:73-90 after rejection:
await rejectTool({ toolId });
// Send rejection email
try {
await fetch('/api/send-email', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'rejection',
recipientUserId: tool.submittedBy,
preferenceKey: 'reviewStatusUpdates',
data: {
toolName: tool.name,
reason: rejectReason.trim()
}
}),
});
} catch (emailError) {
console.error('Failed to send rejection email:', emailError);
}Other Email Templates
The email library also includes templates for:
Submission Confirmation
lib/email.ts:39-67 - Sent when a user submits a new tool:
export function submissionConfirmationEmail(toolName: string)- Confirms receipt of submission
- Explains the review process (1-3 business days)
- Sets expectations for what happens next
Admin Notifications
lib/email.ts:130-157 - Notifies admins of new submissions:
export function adminNotificationEmail(toolName: string, submittedBy: string, adminUrl: string)- Alerts admins to pending reviews
- Includes tool name and submitter
- Contains direct link to admin review page
Error Handling
Email failures are logged but don’t block the approval/rejection process:
try {
await fetch('/api/send-email', { /* ... */ });
} catch (emailError) {
console.error('Failed to send approval email:', emailError);
// Process continues even if email fails
}This ensures that:
- Tool status updates always complete
- Email issues don’t break the admin workflow
- Errors are logged for debugging
- Users aren’t left in limbo if emails fail