Apply full refactor updates plus pipeline/email UX confirmations
All checks were successful
Build and Push Docker Image / build (push) Successful in 10m33s
All checks were successful
Build and Push Docker Image / build (push) Successful in 10m33s
This commit is contained in:
@@ -392,11 +392,14 @@ export default function ProjectsPage() {
|
||||
const [allMatchingSelected, setAllMatchingSelected] = useState(false)
|
||||
const [bulkStatus, setBulkStatus] = useState<string>('')
|
||||
const [bulkConfirmOpen, setBulkConfirmOpen] = useState(false)
|
||||
const [bulkNotificationsConfirmed, setBulkNotificationsConfirmed] = useState(false)
|
||||
const [bulkAction, setBulkAction] = useState<'status' | 'assign' | 'delete'>('status')
|
||||
const [bulkAssignStageId, setBulkAssignStageId] = useState('')
|
||||
const [bulkAssignDialogOpen, setBulkAssignDialogOpen] = useState(false)
|
||||
const [bulkDeleteConfirmOpen, setBulkDeleteConfirmOpen] = useState(false)
|
||||
|
||||
const bulkStatusTriggersNotifications = ['SEMIFINALIST', 'FINALIST', 'REJECTED'].includes(bulkStatus)
|
||||
|
||||
// Query for fetching all matching IDs (used for "select all across pages")
|
||||
const allIdsQuery = trpc.project.listAllIds.useQuery(
|
||||
{
|
||||
@@ -452,6 +455,26 @@ export default function ProjectsPage() {
|
||||
},
|
||||
})
|
||||
|
||||
const bulkNotificationPreview = trpc.project.previewStatusNotificationRecipients.useQuery(
|
||||
{
|
||||
ids: Array.from(selectedIds),
|
||||
status: (bulkStatus || 'SUBMITTED') as
|
||||
| 'SUBMITTED'
|
||||
| 'ELIGIBLE'
|
||||
| 'ASSIGNED'
|
||||
| 'SEMIFINALIST'
|
||||
| 'FINALIST'
|
||||
| 'REJECTED',
|
||||
},
|
||||
{
|
||||
enabled:
|
||||
bulkConfirmOpen &&
|
||||
selectedIds.size > 0 &&
|
||||
bulkStatusTriggersNotifications,
|
||||
staleTime: 30_000,
|
||||
}
|
||||
)
|
||||
|
||||
const bulkAssignToStage = trpc.projectPool.assignToStage.useMutation({
|
||||
onSuccess: (result) => {
|
||||
toast.success(`${result.assignedCount} project${result.assignedCount !== 1 ? 's' : ''} assigned to stage`)
|
||||
@@ -524,15 +547,21 @@ export default function ProjectsPage() {
|
||||
setSelectedIds(new Set())
|
||||
setAllMatchingSelected(false)
|
||||
setBulkStatus('')
|
||||
setBulkNotificationsConfirmed(false)
|
||||
}
|
||||
|
||||
const handleBulkApply = () => {
|
||||
if (!bulkStatus || selectedIds.size === 0) return
|
||||
setBulkNotificationsConfirmed(false)
|
||||
setBulkConfirmOpen(true)
|
||||
}
|
||||
|
||||
const handleBulkConfirm = () => {
|
||||
if (!bulkStatus || selectedIds.size === 0) return
|
||||
if (bulkStatusTriggersNotifications && !bulkNotificationsConfirmed) {
|
||||
toast.error('Confirm participant recipients before sending notifications')
|
||||
return
|
||||
}
|
||||
bulkUpdateStatus.mutate({
|
||||
ids: Array.from(selectedIds),
|
||||
status: bulkStatus as 'SUBMITTED' | 'ELIGIBLE' | 'ASSIGNED' | 'SEMIFINALIST' | 'FINALIST' | 'REJECTED',
|
||||
@@ -1283,7 +1312,15 @@ export default function ProjectsPage() {
|
||||
)}
|
||||
|
||||
{/* Bulk Status Update Confirmation Dialog */}
|
||||
<AlertDialog open={bulkConfirmOpen} onOpenChange={setBulkConfirmOpen}>
|
||||
<AlertDialog
|
||||
open={bulkConfirmOpen}
|
||||
onOpenChange={(open) => {
|
||||
setBulkConfirmOpen(open)
|
||||
if (!open) {
|
||||
setBulkNotificationsConfirmed(false)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Update Project Status</AlertDialogTitle>
|
||||
@@ -1302,6 +1339,64 @@ export default function ProjectsPage() {
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{bulkStatusTriggersNotifications && (
|
||||
<div className="space-y-3 rounded-md border bg-muted/20 p-3">
|
||||
<p className="text-sm font-medium">Participant Notification Check</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Review recipients before automated emails are sent.
|
||||
</p>
|
||||
|
||||
{bulkNotificationPreview.isLoading ? (
|
||||
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
||||
<Loader2 className="h-3.5 w-3.5 animate-spin" />
|
||||
Loading recipients...
|
||||
</div>
|
||||
) : bulkNotificationPreview.data ? (
|
||||
<div className="space-y-2">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{bulkNotificationPreview.data.totalRecipients} recipient
|
||||
{bulkNotificationPreview.data.totalRecipients !== 1 ? 's' : ''} across{' '}
|
||||
{bulkNotificationPreview.data.projectsWithRecipients} project
|
||||
{bulkNotificationPreview.data.projectsWithRecipients !== 1 ? 's' : ''}.
|
||||
</p>
|
||||
|
||||
<div className="max-h-44 space-y-2 overflow-auto rounded-md border bg-background p-2">
|
||||
{bulkNotificationPreview.data.projects
|
||||
.filter((project) => project.recipientCount > 0)
|
||||
.slice(0, 8)
|
||||
.map((project) => (
|
||||
<div key={project.id} className="text-xs">
|
||||
<p className="font-medium">
|
||||
{project.title} ({project.recipientCount})
|
||||
</p>
|
||||
<p className="text-muted-foreground">
|
||||
{project.recipientsPreview.join(', ')}
|
||||
{project.hasMoreRecipients ? ', ...' : ''}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
{bulkNotificationPreview.data.projectsWithRecipients === 0 && (
|
||||
<p className="text-xs text-amber-700">
|
||||
No linked participant accounts found. Status will update, but no team notifications will be sent.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="flex items-start gap-2">
|
||||
<Checkbox
|
||||
id="bulk-notification-confirm"
|
||||
checked={bulkNotificationsConfirmed}
|
||||
onCheckedChange={(checked) => setBulkNotificationsConfirmed(checked === true)}
|
||||
/>
|
||||
<Label htmlFor="bulk-notification-confirm" className="text-sm font-normal leading-5">
|
||||
I verified the recipient list and want to send these automated notifications.
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
@@ -1310,12 +1405,12 @@ export default function ProjectsPage() {
|
||||
<AlertDialogAction
|
||||
onClick={handleBulkConfirm}
|
||||
className={bulkStatus === 'REJECTED' ? 'bg-destructive text-destructive-foreground hover:bg-destructive/90' : ''}
|
||||
disabled={bulkUpdateStatus.isPending}
|
||||
disabled={bulkUpdateStatus.isPending || (bulkStatusTriggersNotifications && !bulkNotificationsConfirmed)}
|
||||
>
|
||||
{bulkUpdateStatus.isPending ? (
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
) : null}
|
||||
Update {selectedIds.size} Project{selectedIds.size !== 1 ? 's' : ''}
|
||||
{bulkStatusTriggersNotifications ? 'Update + Notify' : 'Update'} {selectedIds.size} Project{selectedIds.size !== 1 ? 's' : ''}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
|
||||
Reference in New Issue
Block a user