+
Processing...
@@ -494,13 +783,26 @@ onUnmounted(() => {
\ No newline at end of file
diff --git a/server/api/process/dashboard/summary.get.js b/server/api/process/dashboard/summary.get.js
new file mode 100644
index 0000000..ecb5c7c
--- /dev/null
+++ b/server/api/process/dashboard/summary.get.js
@@ -0,0 +1,349 @@
+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();
+ }
+});
\ No newline at end of file