Files
MOPC-Portal/src/components/settings/email-settings-form.tsx

271 lines
8.0 KiB
TypeScript
Raw Normal View History

'use client'
import { useState } from 'react'
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
import { toast } from 'sonner'
import { Loader2, Mail, Send } from 'lucide-react'
import { trpc } from '@/lib/trpc/client'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/form'
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog'
const formSchema = z.object({
smtp_host: z.string().min(1, 'SMTP host is required'),
smtp_port: z.string().regex(/^\d+$/, 'Port must be a number'),
smtp_user: z.string().min(1, 'SMTP user is required'),
smtp_password: z.string().optional(),
email_from: z.string().email('Invalid email address'),
})
type FormValues = z.infer<typeof formSchema>
interface EmailSettingsFormProps {
settings: {
smtp_host?: string
smtp_port?: string
smtp_user?: string
smtp_password?: string
email_from?: string
}
}
export function EmailSettingsForm({ settings }: EmailSettingsFormProps) {
const [testDialogOpen, setTestDialogOpen] = useState(false)
const [testEmail, setTestEmail] = useState('')
const utils = trpc.useUtils()
const form = useForm<FormValues>({
resolver: zodResolver(formSchema),
defaultValues: {
smtp_host: settings.smtp_host || 'localhost',
smtp_port: settings.smtp_port || '587',
smtp_user: settings.smtp_user || '',
smtp_password: '',
email_from: settings.email_from || 'noreply@monaco-opc.com',
},
})
const updateSettings = trpc.settings.updateMultiple.useMutation({
onSuccess: () => {
toast.success('Email settings saved successfully')
utils.settings.getByCategory.invalidate({ category: 'EMAIL' })
},
onError: (error) => {
toast.error(`Failed to save settings: ${error.message}`)
},
})
const sendTestEmail = trpc.settings.testEmailConnection.useMutation({
onSuccess: (result) => {
setTestDialogOpen(false)
if (result.success) {
toast.success('Test email sent successfully')
} else {
toast.error(`Failed to send test email: ${result.error}`)
}
},
onError: (error) => {
toast.error(`Test failed: ${error.message}`)
},
})
const onSubmit = (data: FormValues) => {
const settingsToUpdate = [
{ key: 'smtp_host', value: data.smtp_host },
{ key: 'smtp_port', value: data.smtp_port },
{ key: 'smtp_user', value: data.smtp_user },
{ key: 'email_from', value: data.email_from },
]
if (data.smtp_password && data.smtp_password.trim()) {
settingsToUpdate.push({ key: 'smtp_password', value: data.smtp_password })
}
updateSettings.mutate({ settings: settingsToUpdate })
}
const handleSendTest = () => {
if (!testEmail) {
toast.error('Please enter an email address')
return
}
sendTestEmail.mutate({ testEmail })
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
<div className="rounded-lg border border-amber-200 bg-amber-50 p-4">
<p className="text-sm text-amber-800">
Email settings are typically configured via environment variables. Changes here
will be stored in the database but may be overridden by environment variables.
</p>
</div>
<div className="grid gap-4 md:grid-cols-2">
<FormField
control={form.control}
name="smtp_host"
render={({ field }) => (
<FormItem>
<FormLabel>SMTP Host</FormLabel>
<FormControl>
<Input placeholder="smtp.example.com" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="smtp_port"
render={({ field }) => (
<FormItem>
<FormLabel>SMTP Port</FormLabel>
<FormControl>
<Input placeholder="587" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="grid gap-4 md:grid-cols-2">
<FormField
control={form.control}
name="smtp_user"
render={({ field }) => (
<FormItem>
<FormLabel>SMTP User</FormLabel>
<FormControl>
<Input placeholder="user@example.com" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="smtp_password"
render={({ field }) => (
<FormItem>
<FormLabel>SMTP Password</FormLabel>
<FormControl>
<Input
type="password"
placeholder={settings.smtp_password ? '••••••••' : 'Enter password'}
{...field}
/>
</FormControl>
<FormDescription>
Leave blank to keep existing password
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
</div>
<FormField
control={form.control}
name="email_from"
render={({ field }) => (
<FormItem>
<FormLabel>From Email Address</FormLabel>
<FormControl>
<Input placeholder="noreply@monaco-opc.com" {...field} />
</FormControl>
<FormDescription>
Email address that will appear as the sender
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<div className="flex gap-2">
<Button type="submit" disabled={updateSettings.isPending}>
{updateSettings.isPending ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Saving...
</>
) : (
<>
<Mail className="mr-2 h-4 w-4" />
Save Email Settings
</>
)}
</Button>
<Dialog open={testDialogOpen} onOpenChange={setTestDialogOpen}>
<DialogTrigger asChild>
<Button type="button" variant="outline">
<Send className="mr-2 h-4 w-4" />
Send Test Email
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Send Test Email</DialogTitle>
<DialogDescription>
Enter an email address to receive a test email
</DialogDescription>
</DialogHeader>
<Input
type="email"
placeholder="test@example.com"
value={testEmail}
onChange={(e) => setTestEmail(e.target.value)}
/>
<DialogFooter>
<Button
variant="outline"
onClick={() => setTestDialogOpen(false)}
>
Cancel
</Button>
<Button
onClick={handleSendTest}
disabled={sendTestEmail.isPending}
>
{sendTestEmail.isPending ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Sending...
</>
) : (
'Send Test'
)}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
</form>
</Form>
)
}