Restore manual email notification feature

- Add /api/notifications/send/:docketEntryId endpoint for manual email sending
- Add green 'Send notification' button (mail icon) to each docket entry in admin dashboard
- Implement BCC email functionality for manual notifications
- Add loading states and user feedback for notification sending
- Allow admins to manually trigger email notifications after uploading documents
- Recovered from git reflog commit 1e24aa18
This commit is contained in:
TheMaddax 2025-09-10 18:57:48 -06:00
parent cd835cf31b
commit cefc600230
3 changed files with 114 additions and 6 deletions

View file

@ -16,6 +16,7 @@ import docketRoutes from './routes/docket';
import documentRoutes from './routes/documents';
import subscriptionRoutes from './routes/subscriptions';
import healthRoutes from './routes/health';
import notificationRoutes from './routes/notifications';
// Load environment variables
dotenv.config();
@ -110,6 +111,7 @@ app.use('/api/auth', authRoutes);
app.use('/api/docket-entries', docketRoutes);
app.use('/api/documents', documentRoutes);
app.use('/api/subscriptions', subscriptionRoutes);
app.use('/api/notifications', notificationRoutes);
// 404 handler
app.use('*', (req, res) => {

View file

@ -0,0 +1,59 @@
import { Router, Response } from 'express';
import { authenticateToken, AuthenticatedRequest } from '../middleware/auth';
import { emailService } from '../services/emailService';
import { logger } from '../utils/logger';
const router = Router();
// Manual email notification endpoint
router.post('/send/:docketEntryId', authenticateToken, async (req: AuthenticatedRequest, res: Response) => {
try {
const { docketEntryId } = req.params;
// Import prisma here to avoid circular dependency
const { prisma } = await import('../index');
// Get the docket entry with details
const docketEntry = await prisma.docketEntry.findUnique({
where: { id: parseInt(docketEntryId) },
include: {
documents: {
orderBy: { displayOrder: 'asc' }
}
}
});
if (!docketEntry) {
return res.status(404).json({
success: false,
message: 'Docket entry not found'
});
}
// Send the email notification using the BCC functionality
await emailService.notifySubscribersOfNewEntry(
docketEntry.title,
docketEntry.date.toISOString(),
docketEntry.summary
);
logger.info(`Manual email notification sent for docket entry: ${docketEntry.title}`, {
docketEntryId: docketEntry.id,
adminUser: req.user?.username
});
res.json({
success: true,
message: 'Email notification sent successfully to all subscribers'
});
} catch (error) {
logger.error('Failed to send manual email notification:', error);
res.status(500).json({
success: false,
message: 'Failed to send email notification'
});
}
});
export default router;

View file

@ -2,11 +2,11 @@
import { useState, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import {
FileText,
Plus,
Calendar,
Users,
import {
FileText,
Plus,
Calendar,
Users,
Upload,
LogOut,
Eye,
@ -14,7 +14,8 @@ import {
Trash2,
X,
AlertCircle,
CheckCircle
CheckCircle,
Mail
} from 'lucide-react';
interface DocketEntry {
@ -37,6 +38,7 @@ export default function AdminDashboard() {
const [editingEntry, setEditingEntry] = useState<DocketEntry | null>(null);
const [showDeleteModal, setShowDeleteModal] = useState(false);
const [deletingEntry, setDeletingEntry] = useState<DocketEntry | null>(null);
const [sendingNotification, setSendingNotification] = useState<number | null>(null);
const router = useRouter();
// Form states for add/edit modals
@ -239,6 +241,34 @@ export default function AdminDashboard() {
}
};
const handleSendNotification = async (entry: DocketEntry) => {
if (sendingNotification === entry.id) return; // Prevent double-clicking
setSendingNotification(entry.id);
try {
const token = localStorage.getItem('adminToken');
const response = await fetch(`/api/notifications/send/${entry.id}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
},
});
const data = await response.json();
if (response.ok) {
alert(`✅ Email notification sent successfully to all subscribers!\n\n"${entry.title}"`);
} else {
alert(`❌ Failed to send email notification: ${data.message || 'Unknown error'}`);
}
} catch (error) {
alert('❌ Network error. Please try again.');
} finally {
setSendingNotification(null);
}
};
if (loading) {
return (
@ -489,6 +519,23 @@ export default function AdminDashboard() {
</div>
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
<button
onClick={() => handleSendNotification(entry)}
disabled={sendingNotification === entry.id}
style={{
padding: '0.5rem',
backgroundColor: sendingNotification === entry.id ? '#9ca3af' : '#16a34a',
color: 'white',
borderRadius: '0.375rem',
border: 'none',
cursor: sendingNotification === entry.id ? 'not-allowed' : 'pointer',
display: 'flex',
alignItems: 'center'
}}
title="Send email notification to all subscribers"
>
<Mail style={{ width: '1rem', height: '1rem' }} />
</button>
<button
onClick={() => handleEditEntry(entry)}
style={{