Md Afiq Iskandar 35a0bd412e Refactor Navigation and Enhance Form Builder UI
- Simplified navigation structure by consolidating child routes for Process and Form Designers, improving clarity and accessibility.
- Updated Form Builder and Process Builder header styles for a more modern look, including logo and title adjustments.
- Enhanced button styles and layout in the Form Builder and Process Management pages for better user interaction.
- Introduced new dashboard metrics and recent activity sections in Process Management, providing users with quick insights into their processes.
- Improved overall responsiveness and visual consistency across the application.
2025-07-11 14:50:42 +08:00

349 lines
9.0 KiB
JavaScript

import { PrismaClient } from '@prisma/client';
// Initialize Prisma client
const prisma = new PrismaClient();
export default defineEventHandler(async (event) => {
try {
// Get query parameters for filtering (optional)
const query = getQuery(event);
const {
startDate,
endDate,
category,
status
} = query;
// Build date filter
const dateFilter = {};
if (startDate && endDate) {
dateFilter.processCreatedDate = {
gte: new Date(startDate),
lte: new Date(endDate)
};
}
// Build process filter
const processFilter = {
...dateFilter
};
if (category) {
processFilter.processCategory = category;
}
if (status) {
processFilter.processStatus = status;
}
// Get process statistics
const [
totalProcesses,
publishedProcesses,
draftProcesses,
archivedProcesses,
deletedProcesses,
processCategories
] = await Promise.all([
// Total processes
prisma.process.count({
where: processFilter
}),
// Published processes
prisma.process.count({
where: {
...processFilter,
processStatus: 'published'
}
}),
// Draft processes
prisma.process.count({
where: {
...processFilter,
processStatus: 'draft'
}
}),
// Archived processes
prisma.process.count({
where: {
...processFilter,
processStatus: 'archived'
}
}),
// Deleted processes
prisma.process.count({
where: {
...processFilter,
processStatus: 'deleted'
}
}),
// Process categories distribution
prisma.process.groupBy({
by: ['processCategory'],
where: processFilter,
_count: {
processCategory: true
}
})
]);
// Get case instance statistics
const [
totalCases,
activeCases,
completedCases,
casesByStatus
] = await Promise.all([
// Total case instances
prisma.caseInstance.count(),
// Active cases
prisma.caseInstance.count({
where: {
caseStatus: 'active'
}
}),
// Completed cases
prisma.caseInstance.count({
where: {
caseStatus: 'completed'
}
}),
// Cases by status
prisma.caseInstance.groupBy({
by: ['caseStatus'],
_count: {
caseStatus: true
}
})
]);
// Get task statistics
const [
totalTasks,
pendingTasks,
completedTasks,
overdueTasks
] = await Promise.all([
// Total tasks
prisma.task.count(),
// Pending tasks
prisma.task.count({
where: {
taskStatus: 'pending'
}
}),
// Completed tasks
prisma.task.count({
where: {
taskStatus: 'completed'
}
}),
// Overdue tasks (pending tasks created more than 3 days ago)
prisma.task.count({
where: {
taskStatus: 'pending',
taskCreatedDate: {
lt: new Date(Date.now() - 3 * 24 * 60 * 60 * 1000) // 3 days ago
}
}
})
]);
// Calculate average completion time for completed cases
const completedCasesWithTime = await prisma.caseInstance.findMany({
where: {
caseStatus: 'completed',
caseCompletedDate: { not: null }
},
select: {
caseCreatedDate: true,
caseCompletedDate: true
}
});
let averageCompletionTime = 0;
if (completedCasesWithTime.length > 0) {
const totalCompletionTime = completedCasesWithTime.reduce((sum, case_) => {
const diffMs = new Date(case_.caseCompletedDate) - new Date(case_.caseCreatedDate);
const diffHours = diffMs / (1000 * 60 * 60);
return sum + diffHours;
}, 0);
averageCompletionTime = Math.round((totalCompletionTime / completedCasesWithTime.length) * 10) / 10;
}
// Calculate success rate (completed cases / total cases * 100)
const successRate = totalCases > 0 ? Math.round((completedCases / totalCases) * 1000) / 10 : 0;
// Get recent activity (latest case instances and process updates)
const recentCases = await prisma.caseInstance.findMany({
take: 5,
orderBy: {
caseCreatedDate: 'desc'
},
include: {
process: {
select: {
processName: true
}
},
startedBy: {
select: {
userFullName: true,
userUsername: true
}
}
}
});
const recentProcessUpdates = await prisma.process.findMany({
take: 5,
orderBy: {
processModifiedDate: 'desc'
},
where: {
processModifiedDate: { not: null }
},
include: {
creator: {
select: {
userFullName: true,
userUsername: true
}
}
}
});
// Format recent activity
const recentActivity = [];
// Add recent cases
recentCases.forEach(case_ => {
recentActivity.push({
id: `case-${case_.caseID}`,
type: case_.caseStatus === 'completed' ? 'case_completed' : 'case_started',
message: `${case_.process.processName} case ${case_.caseStatus === 'completed' ? 'completed' : 'started'}`,
user: case_.startedBy?.userFullName || case_.startedBy?.userUsername || 'Unknown',
timestamp: case_.caseStatus === 'completed' ? case_.caseCompletedDate : case_.caseCreatedDate
});
});
// Add recent process updates
recentProcessUpdates.forEach(process => {
recentActivity.push({
id: `process-${process.processID}`,
type: process.processStatus === 'published' ? 'process_published' : 'process_updated',
message: `${process.processName} process ${process.processStatus === 'published' ? 'published' : 'updated'}`,
user: process.creator?.userFullName || process.creator?.userUsername || 'Unknown',
timestamp: process.processModifiedDate
});
});
// Sort recent activity by timestamp and take the latest 10
recentActivity.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
const latestActivity = recentActivity.slice(0, 10);
// Get monthly statistics for the last 6 months
const sixMonthsAgo = new Date();
sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6);
const monthlyCases = await prisma.caseInstance.groupBy({
by: ['caseCreatedDate'],
where: {
caseCreatedDate: {
gte: sixMonthsAgo
}
},
_count: {
caseID: true
}
});
// Process monthly data
const monthlyStats = [];
for (let i = 5; i >= 0; i--) {
const date = new Date();
date.setMonth(date.getMonth() - i);
const monthKey = date.toISOString().substring(0, 7); // YYYY-MM format
const monthData = monthlyCases.filter(item =>
item.caseCreatedDate.toISOString().substring(0, 7) === monthKey
);
monthlyStats.push({
month: date.toLocaleString('default', { month: 'short' }),
year: date.getFullYear(),
cases: monthData.length,
completed: 0 // Would need additional query to get completed cases for the month
});
}
// Prepare response
const summary = {
overview: {
totalProcesses,
publishedProcesses,
draftProcesses,
archivedProcesses,
deletedProcesses,
totalCases,
activeCases,
completedCases,
totalTasks,
pendingTasks,
completedTasks,
overdueTasks,
averageCompletionTime,
successRate
},
charts: {
processDistribution: [
{ label: 'Published', value: publishedProcesses, color: '#10B981' },
{ label: 'Draft', value: draftProcesses, color: '#F59E0B' },
{ label: 'Archived', value: archivedProcesses, color: '#6B7280' },
{ label: 'Deleted', value: deletedProcesses, color: '#EF4444' }
],
caseStatus: casesByStatus.map(item => ({
label: item.caseStatus,
value: item._count.caseStatus,
color: item.caseStatus === 'active' ? '#3B82F6' :
item.caseStatus === 'completed' ? '#10B981' : '#6B7280'
})),
categoryDistribution: processCategories.map(item => ({
label: item.processCategory || 'Uncategorized',
value: item._count.processCategory
})),
monthlyTrend: monthlyStats
},
recentActivity: latestActivity,
lastUpdated: new Date().toISOString()
};
return {
success: true,
data: summary
};
} catch (error) {
console.error('Error fetching dashboard summary:', error);
return {
success: false,
error: 'Failed to fetch dashboard summary',
details: process.env.NODE_ENV === 'development' ? error.message : undefined
};
} finally {
await prisma.$disconnect();
}
});