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:
Haqeem Solehan
2025-10-16 16:05:39 +08:00
commit b124ff8092
336 changed files with 94392 additions and 0 deletions

View File

@@ -0,0 +1,76 @@
import prisma from "~/server/utils/prisma";
export default defineEventHandler(async (event) => {
try {
const now = new Date();
const last30Days = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
// Get all channel types
const channelTypes = ['email', 'push', 'sms'];
const channelPerformance = [];
for (const channelType of channelTypes) {
// Get stats for this channel
const [totalSent, successful, failed, pending] = await Promise.all([
prisma.notification_recipients.count({
where: {
channel_type: channelType,
created_at: { gte: last30Days }
}
}),
prisma.notification_recipients.count({
where: {
channel_type: channelType,
status: 'sent',
created_at: { gte: last30Days }
}
}),
prisma.notification_recipients.count({
where: {
channel_type: channelType,
status: 'failed',
created_at: { gte: last30Days }
}
}),
prisma.notification_recipients.count({
where: {
channel_type: channelType,
status: 'pending',
created_at: { gte: last30Days }
}
}),
]);
const successRate = totalSent > 0
? ((successful / totalSent) * 100).toFixed(1)
: 0;
const failureRate = totalSent > 0
? ((failed / totalSent) * 100).toFixed(1)
: 0;
channelPerformance.push({
channel: channelType,
totalSent,
successful,
failed,
pending,
successRate: parseFloat(successRate),
failureRate: parseFloat(failureRate),
});
}
return {
success: true,
data: channelPerformance
};
} catch (error) {
console.error("Error fetching channel performance:", error);
throw createError({
statusCode: 500,
statusMessage: "Failed to fetch channel performance",
data: { error: error.message },
});
} finally {
}
});

View 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 {
}
});

View File

@@ -0,0 +1,65 @@
import prisma from "~/server/utils/prisma";
export default defineEventHandler(async (event) => {
try {
const query = getQuery(event);
const limit = parseInt(query.limit) || 10;
// Fetch recent notifications with related data
const recentNotifications = await prisma.notifications.findMany({
take: limit,
orderBy: {
created_at: 'desc'
},
include: {
notification_categories: {
select: {
name: true,
value: true
}
},
notification_channels: {
select: {
channel_type: true
}
},
_count: {
select: {
notification_recipients: true
}
}
}
});
// Format the response
const formatted = recentNotifications.map(notif => ({
id: notif.id,
title: notif.title,
type: notif.type,
priority: notif.priority,
status: notif.status,
category: notif.notification_categories?.name || 'Unknown',
channels: notif.notification_channels.map(ch => ch.channel_type),
recipientCount: notif._count.notification_recipients,
estimatedReach: notif.estimated_reach,
actualSent: notif.actual_sent,
createdAt: notif.created_at,
sentAt: notif.sent_at,
scheduledAt: notif.scheduled_at,
createdBy: notif.created_by,
}));
return {
success: true,
data: formatted
};
} catch (error) {
console.error("Error fetching recent notifications:", error);
throw createError({
statusCode: 500,
statusMessage: "Failed to fetch recent notifications",
data: { error: error.message },
});
} finally {
}
});