Add styled notification emails and round-attached notifications
- Add 15+ styled email templates matching existing invite email design - Wire up notification triggers in all routers (assignment, round, project, mentor, application, onboarding) - Add test email button for each notification type in admin settings - Add round-attached notifications: admins can configure which notification to send when projects enter a round - Fall back to status-based notifications when round has no configured notification Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,7 @@ import { Button } from '@/components/ui/button'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { toast } from 'sonner'
|
||||
import { Users, Scale, GraduationCap, Eye, Shield } from 'lucide-react'
|
||||
import { Users, Scale, GraduationCap, Eye, Shield, Mail, Loader2 } from 'lucide-react'
|
||||
|
||||
// Category icons and labels
|
||||
const CATEGORIES = {
|
||||
@@ -29,6 +29,7 @@ type NotificationSetting = {
|
||||
}
|
||||
|
||||
export function NotificationSettingsForm() {
|
||||
const [testingType, setTestingType] = useState<string | null>(null)
|
||||
const { data: settings, isLoading, refetch } = trpc.notification.getEmailSettings.useQuery()
|
||||
const updateMutation = trpc.notification.updateEmailSetting.useMutation({
|
||||
onSuccess: () => {
|
||||
@@ -40,10 +41,34 @@ export function NotificationSettingsForm() {
|
||||
},
|
||||
})
|
||||
|
||||
const testMutation = trpc.notification.sendTestEmail.useMutation({
|
||||
onSuccess: (data) => {
|
||||
if (data.success) {
|
||||
toast.success(data.message, {
|
||||
description: data.hasStyledTemplate
|
||||
? 'Using styled template'
|
||||
: 'Using generic template',
|
||||
})
|
||||
} else {
|
||||
toast.error('Failed to send test email', { description: data.message })
|
||||
}
|
||||
setTestingType(null)
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(`Failed to send: ${error.message}`)
|
||||
setTestingType(null)
|
||||
},
|
||||
})
|
||||
|
||||
const handleToggle = (notificationType: string, sendEmail: boolean) => {
|
||||
updateMutation.mutate({ notificationType, sendEmail })
|
||||
}
|
||||
|
||||
const handleTest = (notificationType: string) => {
|
||||
setTestingType(notificationType)
|
||||
testMutation.mutate({ notificationType })
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
@@ -112,7 +137,7 @@ export function NotificationSettingsForm() {
|
||||
key={setting.id}
|
||||
className="flex items-center justify-between rounded-lg border p-3"
|
||||
>
|
||||
<div className="space-y-0.5">
|
||||
<div className="space-y-0.5 flex-1 min-w-0">
|
||||
<Label className="text-sm font-medium">
|
||||
{setting.label}
|
||||
</Label>
|
||||
@@ -122,13 +147,30 @@ export function NotificationSettingsForm() {
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<Switch
|
||||
checked={setting.sendEmail}
|
||||
onCheckedChange={(checked) =>
|
||||
handleToggle(setting.notificationType, checked)
|
||||
}
|
||||
disabled={updateMutation.isPending}
|
||||
/>
|
||||
<div className="flex items-center gap-3 ml-4">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-8 px-2 text-muted-foreground hover:text-foreground"
|
||||
onClick={() => handleTest(setting.notificationType)}
|
||||
disabled={testingType === setting.notificationType}
|
||||
title="Send test email to yourself"
|
||||
>
|
||||
{testingType === setting.notificationType ? (
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
<Mail className="h-4 w-4" />
|
||||
)}
|
||||
<span className="ml-1.5 text-xs">Test</span>
|
||||
</Button>
|
||||
<Switch
|
||||
checked={setting.sendEmail}
|
||||
onCheckedChange={(checked) =>
|
||||
handleToggle(setting.notificationType, checked)
|
||||
}
|
||||
disabled={updateMutation.isPending}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</CardContent>
|
||||
|
||||
Reference in New Issue
Block a user