feat: round finalization with ranking-based outcomes + award pool notifications
All checks were successful
Build and Push Docker Image / build (push) Successful in 10m0s
All checks were successful
Build and Push Docker Image / build (push) Successful in 10m0s
- processRoundClose EVALUATION uses ranking scores + advanceMode config (threshold vs count) to auto-set proposedOutcome instead of defaulting all to PASSED - Advancement emails generate invite tokens for passwordless users with "Create Your Account" CTA; rejection emails have no link - Finalization UI shows account stats (invite vs dashboard link counts) - Fixed getFinalizationSummary ranking query (was using non-existent rankingsJson) - New award pool notification system: getAwardSelectionNotificationTemplate email, notifyEligibleProjects mutation with invite token generation, "Notify Pool" button on award detail page with custom message dialog Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
62
src/components/admin/round/notify-advanced-button.tsx
Normal file
62
src/components/admin/round/notify-advanced-button.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { trpc } from '@/lib/trpc/client'
|
||||
import { toast } from 'sonner'
|
||||
import { Trophy } from 'lucide-react'
|
||||
import { EmailPreviewDialog } from './email-preview-dialog'
|
||||
|
||||
interface NotifyAdvancedButtonProps {
|
||||
roundId: string
|
||||
targetRoundId?: string
|
||||
}
|
||||
|
||||
export function NotifyAdvancedButton({ roundId, targetRoundId }: NotifyAdvancedButtonProps) {
|
||||
const [open, setOpen] = useState(false)
|
||||
const [customMessage, setCustomMessage] = useState<string | undefined>()
|
||||
|
||||
const preview = trpc.round.previewAdvancementEmail.useQuery(
|
||||
{ roundId, targetRoundId, customMessage },
|
||||
{ enabled: open }
|
||||
)
|
||||
|
||||
const sendMutation = trpc.round.sendAdvancementNotifications.useMutation({
|
||||
onSuccess: (data) => {
|
||||
toast.success(
|
||||
`Sent ${data.sent} notification${data.sent !== 1 ? 's' : ''}${data.failed ? ` (${data.failed} failed)` : ''}`
|
||||
)
|
||||
setOpen(false)
|
||||
},
|
||||
onError: (err) => toast.error(err.message),
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
onClick={() => setOpen(true)}
|
||||
className="flex items-start gap-3 p-4 rounded-lg border border-l-4 border-l-emerald-500 hover:-translate-y-0.5 hover:shadow-md transition-all text-left"
|
||||
>
|
||||
<Trophy className="h-5 w-5 text-emerald-600 mt-0.5 shrink-0" />
|
||||
<div>
|
||||
<p className="text-sm font-medium">Notify Advanced Teams</p>
|
||||
<p className="text-xs text-muted-foreground mt-0.5">
|
||||
Send advancement emails to passed projects
|
||||
</p>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<EmailPreviewDialog
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
title="Notify Advanced Teams"
|
||||
description="Send advancement notification emails to project team members"
|
||||
recipientCount={preview.data?.recipientCount ?? 0}
|
||||
previewHtml={preview.data?.html}
|
||||
isPreviewLoading={preview.isLoading}
|
||||
onSend={(msg) => sendMutation.mutate({ roundId, targetRoundId, customMessage: msg })}
|
||||
isSending={sendMutation.isPending}
|
||||
onRefreshPreview={(msg) => setCustomMessage(msg)}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user