Update various configuration files, components, and assets; enhance notification system and API endpoints; improve documentation and styles across the application.
This commit is contained in:
170
server/api/notifications/dashboard/overview.get.js
Normal file
170
server/api/notifications/dashboard/overview.get.js
Normal file
@@ -0,0 +1,170 @@
|
||||
import prisma from "~/server/utils/prisma";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
const now = new Date();
|
||||
const last24Hours = new Date(now.getTime() - 24 * 60 * 60 * 1000);
|
||||
const last7Days = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
|
||||
const last30Days = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
|
||||
|
||||
// Fetch comprehensive stats
|
||||
const [
|
||||
totalNotifications,
|
||||
totalSent,
|
||||
totalScheduled,
|
||||
totalDraft,
|
||||
sentLast24h,
|
||||
sentLast7Days,
|
||||
totalRecipients,
|
||||
successfulDeliveries,
|
||||
failedDeliveries,
|
||||
queuedJobs,
|
||||
channelStats,
|
||||
categoryStats,
|
||||
] = await Promise.all([
|
||||
// Total notifications
|
||||
prisma.notifications.count(),
|
||||
|
||||
// Total sent notifications
|
||||
prisma.notifications.count({
|
||||
where: { status: "sending" }
|
||||
}),
|
||||
|
||||
// Total scheduled
|
||||
prisma.notifications.count({
|
||||
where: { status: "scheduled" }
|
||||
}),
|
||||
|
||||
// Total drafts
|
||||
prisma.notifications.count({
|
||||
where: { status: "draft" }
|
||||
}),
|
||||
|
||||
// Sent in last 24 hours
|
||||
prisma.notifications.count({
|
||||
where: {
|
||||
status: "sending",
|
||||
sent_at: { gte: last24Hours }
|
||||
}
|
||||
}),
|
||||
|
||||
// Sent in last 7 days
|
||||
prisma.notifications.count({
|
||||
where: {
|
||||
status: "sending",
|
||||
sent_at: { gte: last7Days }
|
||||
}
|
||||
}),
|
||||
|
||||
// Total recipients
|
||||
prisma.notification_recipients.count(),
|
||||
|
||||
// Successful deliveries
|
||||
prisma.notification_recipients.count({
|
||||
where: { status: "sent" }
|
||||
}),
|
||||
|
||||
// Failed deliveries
|
||||
prisma.notification_recipients.count({
|
||||
where: { status: "failed" }
|
||||
}),
|
||||
|
||||
// Queued jobs
|
||||
prisma.notification_queue.count({
|
||||
where: { status: "queued" }
|
||||
}),
|
||||
|
||||
// Channel distribution
|
||||
prisma.notification_channels.groupBy({
|
||||
by: ['channel_type'],
|
||||
_count: {
|
||||
channel_type: true
|
||||
}
|
||||
}),
|
||||
|
||||
// Category distribution
|
||||
prisma.notifications.groupBy({
|
||||
by: ['category_id'],
|
||||
_count: {
|
||||
category_id: true
|
||||
},
|
||||
take: 5,
|
||||
orderBy: {
|
||||
_count: {
|
||||
category_id: 'desc'
|
||||
}
|
||||
}
|
||||
}),
|
||||
]);
|
||||
|
||||
// Calculate delivery rate
|
||||
const totalDeliveryAttempts = successfulDeliveries + failedDeliveries;
|
||||
const deliveryRate = totalDeliveryAttempts > 0
|
||||
? ((successfulDeliveries / totalDeliveryAttempts) * 100).toFixed(1)
|
||||
: 100;
|
||||
|
||||
// Calculate growth rate (last 7 days vs previous 7 days)
|
||||
const previous7Days = new Date(last7Days.getTime() - 7 * 24 * 60 * 60 * 1000);
|
||||
const sentPrevious7Days = await prisma.notifications.count({
|
||||
where: {
|
||||
status: "sending",
|
||||
sent_at: {
|
||||
gte: previous7Days,
|
||||
lt: last7Days
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const growthRate = sentPrevious7Days > 0
|
||||
? (((sentLast7Days - sentPrevious7Days) / sentPrevious7Days) * 100).toFixed(1)
|
||||
: 0;
|
||||
|
||||
// Get category names
|
||||
const categoryIds = categoryStats.map(c => c.category_id).filter(Boolean);
|
||||
const categories = await prisma.notification_categories.findMany({
|
||||
where: { id: { in: categoryIds } },
|
||||
select: { id: true, name: true }
|
||||
});
|
||||
|
||||
const categoryMap = Object.fromEntries(categories.map(c => [c.id, c.name]));
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
overview: {
|
||||
total: totalNotifications,
|
||||
sent: totalSent,
|
||||
scheduled: totalScheduled,
|
||||
draft: totalDraft,
|
||||
sentLast24h,
|
||||
sentLast7Days,
|
||||
growthRate: parseFloat(growthRate),
|
||||
},
|
||||
delivery: {
|
||||
totalRecipients,
|
||||
successful: successfulDeliveries,
|
||||
failed: failedDeliveries,
|
||||
deliveryRate: parseFloat(deliveryRate),
|
||||
queued: queuedJobs,
|
||||
},
|
||||
channels: channelStats.map(ch => ({
|
||||
channel: ch.channel_type,
|
||||
count: ch._count.channel_type,
|
||||
})),
|
||||
topCategories: categoryStats.map(cat => ({
|
||||
categoryId: cat.category_id,
|
||||
categoryName: categoryMap[cat.category_id] || 'Unknown',
|
||||
count: cat._count.category_id,
|
||||
})),
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error fetching dashboard overview:", error);
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: "Failed to fetch dashboard data",
|
||||
data: { error: error.message },
|
||||
});
|
||||
} finally {
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user