diff --git a/src/components/admin/VolunteerTable.js b/src/components/admin/VolunteerTable.js
index 293bd42..616c2d0 100644
--- a/src/components/admin/VolunteerTable.js
+++ b/src/components/admin/VolunteerTable.js
@@ -1,4 +1,4 @@
-import React, { useMemo, useState, useCallback } from "react";
+import React, { useMemo, useState, useCallback, useEffect } from "react";
import {
Table,
TableBody,
@@ -205,6 +205,8 @@ const VolunteerTable = ({
const [copyFeedback, setCopyFeedback] = useState({ open: false, message: '' });
const [resendStatuses, setResendStatuses] = useState({}); // { resend_id: { last_event, ... } }
const [loadingResendStatus, setLoadingResendStatus] = useState({});
+ const [resendEmailsByRecipient, setResendEmailsByRecipient] = useState({}); // { email: [{id, subject, created_at, last_event}] }
+ const [resendListLoaded, setResendListLoaded] = useState(false);
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
const isSmallMobile = useMediaQuery(theme.breakpoints.down('sm'));
@@ -285,6 +287,49 @@ const VolunteerTable = ({
}
}, [accessToken, orgId, resendStatuses, loadingResendStatus]);
+ // Fetch all sent emails from Resend list API, indexed by recipient
+ const fetchResendEmailList = useCallback(async () => {
+ if (!accessToken || !orgId || !volunteers?.length) return;
+
+ const uniqueEmails = [...new Set(
+ volunteers
+ .map(v => v.email)
+ .filter(e => e && e.includes('@'))
+ )];
+
+ if (uniqueEmails.length === 0) return;
+
+ try {
+ const response = await fetch(
+ `${process.env.NEXT_PUBLIC_API_SERVER_URL}/api/admin/emails/resend-list?emails=${encodeURIComponent(uniqueEmails.join(','))}`,
+ {
+ headers: {
+ Authorization: `Bearer ${accessToken}`,
+ 'X-Org-Id': orgId,
+ },
+ }
+ );
+
+ if (response.ok) {
+ const data = await response.json();
+ const emailsByRecipient = data?.data?.emails_by_recipient || data?.emails_by_recipient;
+ if (emailsByRecipient) {
+ setResendEmailsByRecipient(emailsByRecipient);
+ }
+ }
+ } catch (err) {
+ console.warn('Failed to fetch Resend email list:', err);
+ }
+ }, [accessToken, orgId, volunteers]);
+
+ // Fetch Resend email list once when volunteers load
+ useEffect(() => {
+ if (!resendListLoaded && volunteers?.length > 0 && accessToken && orgId) {
+ setResendListLoaded(true);
+ fetchResendEmailList();
+ }
+ }, [volunteers, accessToken, orgId, resendListLoaded, fetchResendEmailList]);
+
// Helper to get sent emails from either new sent_emails or legacy messages_sent
const getSentEmails = useCallback((volunteer) => {
if (Array.isArray(volunteer.sent_emails) && volunteer.sent_emails.length > 0) {
@@ -571,7 +616,25 @@ const VolunteerTable = ({
const sentEmails = getSentEmails(volunteer);
const messageCount = sentEmails.length;
- if (messageCount === 0) {
+ // Look up emails from Resend list API for this volunteer
+ const volunteerEmail = volunteer.email?.toLowerCase();
+ const resendListEmails = volunteerEmail ? (resendEmailsByRecipient[volunteerEmail] || []) : [];
+
+ // Delivery status chip rendering helper (shared by stored and list-based emails)
+ const eventMap = {
+ delivered: { label: 'Delivered', color: 'success' },
+ sent: { label: 'Sent', color: 'info' },
+ bounced: { label: 'Bounced', color: 'error' },
+ complained: { label: 'Complained', color: 'error' },
+ delivery_delayed: { label: 'Delayed', color: 'warning' },
+ };
+
+ const renderResendEventChip = (lastEvent) => {
+ const display = eventMap[lastEvent] || { label: lastEvent || 'Unknown', color: 'default' };
+ return ;
+ };
+
+ if (messageCount === 0 && resendListEmails.length === 0) {
return (
{
- // Check for Resend live status first
+ // Check for Resend live status first (from per-ID fetch)
if (email.resend_id && resendStatuses[email.resend_id]) {
const status = resendStatuses[email.resend_id];
- const eventMap = {
- delivered: { label: 'Delivered', color: 'success' },
- sent: { label: 'Sent', color: 'info' },
- bounced: { label: 'Bounced', color: 'error' },
- complained: { label: 'Complained', color: 'error' },
- delivery_delayed: { label: 'Delayed', color: 'warning' },
- };
- const display = eventMap[status.last_event] || { label: status.last_event || 'Unknown', color: 'default' };
- return ;
+ return renderResendEventChip(status.last_event);
}
// Loading state
if (email.resend_id && loadingResendStatus[email.resend_id]) {
return ;
}
+ // Try matching from Resend list data by subject when no resend_id
+ if (!email.resend_id && email.subject && resendListEmails.length > 0) {
+ const match = resendListEmails.find(re => re.subject === email.subject);
+ if (match) {
+ return renderResendEventChip(match.last_event);
+ }
+ }
// Legacy delivery_status fallback
if (email._legacy_delivery_status) {
const ds = email._legacy_delivery_status;
@@ -620,13 +682,18 @@ const VolunteerTable = ({
return null;
};
+ // Determine display count: stored messages + any additional from Resend list
+ const storedResendIds = new Set(sentEmails.filter(e => e.resend_id).map(e => e.resend_id));
+ const additionalResendEmails = resendListEmails.filter(re => !storedResendIds.has(re.id));
+ const totalDisplayCount = messageCount + additionalResendEmails.length;
+
const messagesToolTipContent = (
- Recent Messages ({messageCount})
+ Recent Messages ({totalDisplayCount})
{sentEmails.slice(0, 5).map((email, idx) => (
-
+
{new Date(email.timestamp).toLocaleString()}
@@ -643,15 +710,40 @@ const VolunteerTable = ({
))}
+ {additionalResendEmails.length > 0 && (
+ <>
+
+ Via Resend
+
+ {additionalResendEmails.slice(0, 5).map((re, idx) => (
+
+
+ {new Date(re.created_at).toLocaleString()}
+
+
+ Subject: {re.subject}
+
+
+ {renderResendEventChip(re.last_event)}
+
+
+ ))}
+ {additionalResendEmails.length > 5 && (
+
+ ...and {additionalResendEmails.length - 5} more from Resend
+
+ )}
+ >
+ )}
{messageCount > 5 && (
- ...and {messageCount - 5} more messages
+ ...and {messageCount - 5} more stored messages
)}
);
- // Fetch Resend statuses when the tooltip opens
+ // Fetch Resend statuses when the tooltip opens (supplementary per-ID fetch)
const handleTooltipOpen = () => {
const resendIds = sentEmails
.filter(e => e.resend_id)
@@ -679,7 +771,7 @@ const VolunteerTable = ({
}}
>