feat: add email preview to award notification and finalization tab
- Award "Notify Pool" dialog now uses EmailPreviewDialog with live preview - Button shows eligible project count: "Notify Pool (38)" - Finalization tab email section has "Preview" buttons for both advancement and rejection messages - EmailPreviewDialog supports previewOnly mode (close button, no send) - Backend: previewAwardSelectionEmail, previewFinalizationAdvancementEmail, previewFinalizationRejectionEmail queries Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -39,9 +39,11 @@ import {
|
||||
ChevronRight,
|
||||
Mail,
|
||||
Send,
|
||||
Eye,
|
||||
} from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { projectStateConfig } from '@/lib/round-config'
|
||||
import { EmailPreviewDialog } from './email-preview-dialog'
|
||||
|
||||
// ── Types ──────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -81,6 +83,10 @@ export function FinalizationTab({ roundId, roundStatus }: FinalizationTabProps)
|
||||
const [emailSectionOpen, setEmailSectionOpen] = useState(false)
|
||||
const [advancementMessage, setAdvancementMessage] = useState('')
|
||||
const [rejectionMessage, setRejectionMessage] = useState('')
|
||||
const [advancePreviewOpen, setAdvancePreviewOpen] = useState(false)
|
||||
const [rejectPreviewOpen, setRejectPreviewOpen] = useState(false)
|
||||
const [advancePreviewMsg, setAdvancePreviewMsg] = useState<string | undefined>()
|
||||
const [rejectPreviewMsg, setRejectPreviewMsg] = useState<string | undefined>()
|
||||
|
||||
// Mutations
|
||||
const updateOutcome = trpc.roundEngine.updateProposedOutcome.useMutation({
|
||||
@@ -121,6 +127,16 @@ export function FinalizationTab({ roundId, roundStatus }: FinalizationTabProps)
|
||||
onError: (err) => toast.error(err.message),
|
||||
})
|
||||
|
||||
// Email preview queries
|
||||
const advancePreview = trpc.roundEngine.previewFinalizationAdvancementEmail.useQuery(
|
||||
{ roundId, customMessage: advancePreviewMsg },
|
||||
{ enabled: advancePreviewOpen }
|
||||
)
|
||||
const rejectPreview = trpc.roundEngine.previewFinalizationRejectionEmail.useQuery(
|
||||
{ roundId, customMessage: rejectPreviewMsg },
|
||||
{ enabled: rejectPreviewOpen }
|
||||
)
|
||||
|
||||
// Filtered projects
|
||||
const filteredProjects = useMemo(() => {
|
||||
if (!summary) return []
|
||||
@@ -624,7 +640,21 @@ export function FinalizationTab({ roundId, roundStatus }: FinalizationTabProps)
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<label className="text-sm font-medium mb-1.5 block">Advancement Message</label>
|
||||
<div className="flex items-center justify-between mb-1.5">
|
||||
<label className="text-sm font-medium">Advancement Message</label>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-7 text-xs"
|
||||
onClick={() => {
|
||||
setAdvancePreviewMsg(advancementMessage || undefined)
|
||||
setAdvancePreviewOpen(true)
|
||||
}}
|
||||
>
|
||||
<Eye className="h-3.5 w-3.5 mr-1" />
|
||||
Preview
|
||||
</Button>
|
||||
</div>
|
||||
<Textarea
|
||||
placeholder="Custom message for projects that are advancing (added to the standard email template)..."
|
||||
value={advancementMessage}
|
||||
@@ -633,7 +663,21 @@ export function FinalizationTab({ roundId, roundStatus }: FinalizationTabProps)
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium mb-1.5 block">Rejection Message</label>
|
||||
<div className="flex items-center justify-between mb-1.5">
|
||||
<label className="text-sm font-medium">Rejection Message</label>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-7 text-xs"
|
||||
onClick={() => {
|
||||
setRejectPreviewMsg(rejectionMessage || undefined)
|
||||
setRejectPreviewOpen(true)
|
||||
}}
|
||||
>
|
||||
<Eye className="h-3.5 w-3.5 mr-1" />
|
||||
Preview
|
||||
</Button>
|
||||
</div>
|
||||
<Textarea
|
||||
placeholder="Custom message for projects that are not advancing (added to the standard email template)..."
|
||||
value={rejectionMessage}
|
||||
@@ -718,6 +762,34 @@ export function FinalizationTab({ roundId, roundStatus }: FinalizationTabProps)
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Email Preview Dialogs */}
|
||||
<EmailPreviewDialog
|
||||
open={advancePreviewOpen}
|
||||
onOpenChange={setAdvancePreviewOpen}
|
||||
title="Advancement Email Preview"
|
||||
description="Preview of the email sent to advancing project teams"
|
||||
recipientCount={advancePreview.data?.recipientCount ?? passedCount}
|
||||
previewHtml={advancePreview.data?.html}
|
||||
isPreviewLoading={advancePreview.isLoading}
|
||||
onSend={() => setAdvancePreviewOpen(false)}
|
||||
isSending={false}
|
||||
showCustomMessage={false}
|
||||
previewOnly
|
||||
/>
|
||||
<EmailPreviewDialog
|
||||
open={rejectPreviewOpen}
|
||||
onOpenChange={setRejectPreviewOpen}
|
||||
title="Rejection Email Preview"
|
||||
description="Preview of the email sent to non-advancing project teams"
|
||||
recipientCount={rejectPreview.data?.recipientCount ?? rejectedCount}
|
||||
previewHtml={rejectPreview.data?.html}
|
||||
isPreviewLoading={rejectPreview.isLoading}
|
||||
onSend={() => setRejectPreviewOpen(false)}
|
||||
isSending={false}
|
||||
showCustomMessage={false}
|
||||
previewOnly
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user