feat: Add mail test functionality

Adds a dialog to test email settings and integrates with the `/api/settings/test/email` API endpoint.
- Added a "Test Email" button to the Mail Settings tab.
- Implemented a dialog for entering the recipient email and selecting a template.
- Added UI elements for the dialog (input fields, radio buttons, and a send button).
- Updated `src/services/settingsService.ts` to include the test email API call.
- Added UI components for the dialog (using existing UI components).
- Updated `src/components/settings/general/MailSettingsTab.tsx` to include the test email functionality.
This commit is contained in:
Tola Leng
2025-06-05 21:06:48 +08:00
parent a88b356bf5
commit 039a832b79
6 changed files with 325 additions and 42 deletions
@@ -0,0 +1,173 @@
import React, { useState } from 'react';
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { useLanguage } from "@/contexts/LanguageContext";
import { Mail, X } from "lucide-react";
import { toast } from "@/hooks/use-toast";
interface TestEmailDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
onSendTest: (data: TestEmailData) => Promise<void>;
isTesting: boolean;
}
export interface TestEmailData {
email: string;
template: string;
collection?: string;
}
const TestEmailDialog: React.FC<TestEmailDialogProps> = ({
open,
onOpenChange,
onSendTest,
isTesting
}) => {
const { t } = useLanguage();
const [email, setEmail] = useState('');
const [template, setTemplate] = useState('verification');
const [collection, setCollection] = useState('_superusers');
const handleSend = async () => {
if (!email) {
toast({
title: "Error",
description: "Please enter an email address",
variant: "destructive",
});
return;
}
try {
await onSendTest({
email,
template,
collection: template === 'verification' ? collection : undefined
});
toast({
title: "Success",
description: "Test email sent successfully",
variant: "default",
});
// Close dialog on success
handleClose();
} catch (error) {
console.error('Error sending test email:', error);
toast({
title: "Error",
description: "Failed to send test email",
variant: "destructive",
});
}
};
const handleClose = () => {
onOpenChange(false);
// Reset form
setEmail('');
setTemplate('verification');
setCollection('_superusers');
};
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<Mail className="h-5 w-5" />
{t("sendTestEmail", "settings")}
</DialogTitle>
<Button
variant="ghost"
size="icon"
className="absolute right-4 top-4"
onClick={handleClose}
>
<X className="h-4 w-4" />
</Button>
</DialogHeader>
<div className="space-y-4 py-4">
{/* Template Selection */}
<div className="space-y-3">
<Label>{t("emailTemplate", "settings")}</Label>
<RadioGroup value={template} onValueChange={setTemplate}>
<div className="flex items-center space-x-2">
<RadioGroupItem value="verification" id="verification" />
<Label htmlFor="verification">{t("verification", "settings")}</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="password-reset" id="password-reset" />
<Label htmlFor="password-reset">{t("passwordReset", "settings")}</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="email-change" id="email-change" />
<Label htmlFor="email-change">{t("confirmEmailChange", "settings")}</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="otp" id="otp" />
<Label htmlFor="otp">{t("otp", "settings")}</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="login-alert" id="login-alert" />
<Label htmlFor="login-alert">{t("loginAlert", "settings")}</Label>
</div>
</RadioGroup>
</div>
{/* Auth Collection - only show for verification template */}
{template === 'verification' && (
<div className="space-y-2">
<Label>{t("authCollection", "settings")} *</Label>
<Select value={collection} onValueChange={setCollection}>
<SelectTrigger>
<SelectValue placeholder={t("selectCollection", "settings")} />
</SelectTrigger>
<SelectContent>
<SelectItem value="_superusers">_superusers</SelectItem>
<SelectItem value="users">users</SelectItem>
</SelectContent>
</Select>
</div>
)}
{/* Email Address */}
<div className="space-y-2">
<Label>{t("toEmailAddress", "settings")} *</Label>
<Input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder={t("enterEmailAddress", "settings")}
required
/>
</div>
</div>
<DialogFooter className="flex justify-between">
<Button variant="outline" onClick={handleClose}>
{t("close", "common")}
</Button>
<Button
onClick={handleSend}
disabled={!email || isTesting}
className="flex items-center gap-2"
>
<Mail className="h-4 w-4" />
{isTesting ? t("sending", "settings") : t("send", "common")}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
};
export default TestEmailDialog;