Commit 1356dcd8 by Augusto

dashboard endpoints

parent a5a1dd45
......@@ -11,6 +11,7 @@ import { CommentModule } from './modules/comment/comment.module';
import { AttachmentModule } from './modules/attachment/attachment.module';
import { ConclusionModule } from './modules/conclusion/conclusion.module';
import { AssistantModule } from './modules/assistant/assistant.module';
import { DashboardModule } from './modules/dashboard/dashboard.module';
import { JwtAuthGuard } from './modules/auth/guards/jwt-auth.guard';
@Module({
......@@ -26,6 +27,7 @@ import { JwtAuthGuard } from './modules/auth/guards/jwt-auth.guard';
AttachmentModule,
ConclusionModule,
AssistantModule,
DashboardModule,
],
controllers: [AppController],
providers: [
......
......@@ -30,6 +30,7 @@ async function bootstrap() {
.setVersion('1.0')
.addTag('auth', 'Authentication endpoints')
.addTag('users', 'User management endpoints')
.addTag('dashboard', 'Dashboard and analytics endpoints')
.addBearerAuth(
{
type: 'http',
......
# Dashboard Module
This module provides comprehensive analytics and dashboard functionality for the Unike Form application. It aggregates data from all modules to provide insights into system usage, performance metrics, and trends.
## Features
### 📊 Comprehensive Statistics
- **User Statistics**: Total users, users by role, recent registrations
- **Occurrence Statistics**: Total occurrences, status distribution, priority distribution, resolution times
- **Activity Statistics**: Comments, attachments, conclusions, assistants, and logs
- **Top Performers**: Most active reporters, assignees, and commenters
- **Category Analysis**: Occurrence distribution by category with percentages
- **Time Series Data**: Trends over the last 12 months for users and occurrences
### 🔍 Specialized Analytics
- **Occurrence Trends**: Daily occurrence creation patterns
- **Resolution Time Analysis**: Average, minimum, and maximum resolution times by status
- **Location Statistics**: Top locations with most occurrences
- **Dashboard Summary**: High-level overview for all user roles
## API Endpoints
### Main Dashboard Statistics
- `GET /dashboard/stats` - Complete dashboard statistics (ADMIN/MODERATOR only)
- `GET /dashboard/summary` - Basic dashboard summary (All authenticated users)
### Specialized Analytics
- `GET /dashboard/trends/occurrences?days=30` - Occurrence trends over time
- `GET /dashboard/stats/resolution-time` - Resolution time statistics by status
- `GET /dashboard/stats/locations` - Top 10 locations by occurrence count
## Data Aggregated
### User Data
- Total user count
- Users by role (USER, MODERATOR, ADMIN)
- Recent user registrations (last 30 days)
- User registration trends (last 12 months)
### Occurrence Data
- Total occurrence count
- Occurrences by status (OPEN, IN_PROGRESS, RESOLVED, PAUSED, CLOSED, PARCIAL_RESOLVED, CANCELLED)
- Occurrences by priority (LOW, MEDIUM, HIGH, URGENT)
- Recent occurrences (last 30 days)
- Average resolution time
- Occurrences with conclusions
- Occurrences with attachments
- Occurrence creation trends (last 12 months)
### Activity Data
- Total comments count
- Total attachments count
- Total conclusions count
- Total assistants count
- Total occurrence logs count
- Recent activity (last 30 days)
### Performance Metrics
- Top 5 users by reported occurrences
- Top 5 users by assigned occurrences
- Top 5 users by comments
- Category distribution with percentages
- Resolution time statistics by status
- Location-based statistics
## Role-Based Access Control
### ADMIN & MODERATOR Access
- Full dashboard statistics
- All specialized analytics endpoints
- Complete system insights
### USER Access
- Basic dashboard summary only
- High-level statistics without sensitive details
## Performance Optimizations
- **Parallel Queries**: All statistics are fetched in parallel for optimal performance
- **Efficient Aggregations**: Uses database-level aggregations to minimize data transfer
- **Indexed Queries**: Leverages existing database indexes for fast queries
- **Caching Ready**: Service structure supports easy caching implementation
## Database Queries
The module uses Drizzle ORM with MySQL-specific optimizations:
- `COUNT()` aggregations for totals
- `GROUP BY` for categorical distributions
- `DATE_FORMAT()` for time series data
- `TIMESTAMPDIFF()` for resolution time calculations
- `INNER JOIN` for related data aggregation
## Usage Examples
### Get Complete Dashboard
```typescript
GET /dashboard/stats
Authorization: Bearer <jwt-token>
```
### Get Occurrence Trends
```typescript
GET /dashboard/trends/occurrences?days=7
Authorization: Bearer <jwt-token>
```
### Get Basic Summary
```typescript
GET /dashboard/summary
Authorization: Bearer <jwt-token>
```
## Response Structure
### Dashboard Stats Response
```json
{
"users": {
"total": 150,
"byRole": {
"USER": 120,
"MODERATOR": 25,
"ADMIN": 5
},
"recentUsers": 12
},
"occurrences": {
"total": 450,
"byStatus": {
"OPEN": 50,
"IN_PROGRESS": 30,
"RESOLVED": 200,
"CLOSED": 150,
"PAUSED": 10,
"PARCIAL_RESOLVED": 8,
"CANCELLED": 2
},
"byPriority": {
"LOW": 100,
"MEDIUM": 200,
"HIGH": 120,
"URGENT": 30
},
"recentOccurrences": 45,
"averageResolutionTime": 5.2,
"withConclusions": 180,
"withAttachments": 220
},
"activity": {
"totalComments": 1200,
"totalAttachments": 800,
"totalConclusions": 180,
"totalAssistants": 25,
"totalLogs": 3000,
"recentActivity": {
"comments": 150,
"attachments": 80,
"conclusions": 20,
"logs": 400
}
},
"topPerformers": {
"topReporters": [...],
"topAssignees": [...],
"topCommenters": [...]
},
"categories": {
"byCategory": [...]
},
"timeSeries": {
"occurrencesOverTime": [...],
"usersOverTime": [...]
},
"lastUpdated": "2024-01-15T10:30:00Z"
}
```
## Future Enhancements
- Real-time updates via WebSocket
- Custom date range filtering
- Export functionality (PDF, Excel)
- Advanced filtering and search
- Performance metrics and alerts
- Custom dashboard widgets
- Data visualization endpoints
import {
Controller,
Get,
Query,
UseGuards,
HttpStatus,
HttpCode,
} from '@nestjs/common';
import {
ApiTags,
ApiOperation,
ApiResponse,
ApiBearerAuth,
ApiQuery,
} from '@nestjs/swagger';
import { DashboardService } from './dashboard.service';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { RolesGuard } from '../auth/guards/roles.guard';
import { Roles } from '../auth/decorators/roles.decorator';
import { UserRoleEnum } from '../../drizzle/schema';
import { DashboardStatsDto } from './dto/dashboard-stats.dto';
@ApiTags('dashboard')
@Controller('dashboard')
@UseGuards(JwtAuthGuard, RolesGuard)
@ApiBearerAuth('JWT-auth')
export class DashboardController {
constructor(private readonly dashboardService: DashboardService) {}
@Get('stats')
@Roles(UserRoleEnum.ADMIN, UserRoleEnum.MODERATOR)
@HttpCode(HttpStatus.OK)
@ApiOperation({
summary: 'Get comprehensive dashboard statistics',
description:
'Retrieves aggregated statistics from all modules including users, occurrences, activities, and trends. Only accessible by ADMIN and MODERATOR roles.',
})
@ApiResponse({
status: 200,
description: 'Dashboard statistics retrieved successfully',
type: DashboardStatsDto,
})
@ApiResponse({
status: 401,
description: 'Unauthorized - Invalid or missing JWT token',
})
@ApiResponse({
status: 403,
description: 'Forbidden - Insufficient permissions',
})
async getDashboardStats(): Promise<DashboardStatsDto> {
return this.dashboardService.getDashboardStats();
}
@Get('trends/occurrences')
@Roles(UserRoleEnum.ADMIN, UserRoleEnum.MODERATOR)
@HttpCode(HttpStatus.OK)
@ApiOperation({
summary: 'Get occurrence trends over time',
description:
'Retrieves occurrence creation trends for the specified number of days. Only accessible by ADMIN and MODERATOR roles.',
})
@ApiQuery({
name: 'days',
required: false,
type: Number,
description: 'Number of days to look back (default: 30)',
example: 30,
})
@ApiResponse({
status: 200,
description: 'Occurrence trends retrieved successfully',
schema: {
type: 'array',
items: {
type: 'object',
properties: {
date: { type: 'string', format: 'date' },
count: { type: 'number' },
},
},
},
})
@ApiResponse({
status: 401,
description: 'Unauthorized - Invalid or missing JWT token',
})
@ApiResponse({
status: 403,
description: 'Forbidden - Insufficient permissions',
})
async getOccurrenceTrends(@Query('days') days?: number) {
const daysToQuery = days ? parseInt(days.toString(), 10) : 30;
return this.dashboardService.getOccurrenceTrends(daysToQuery);
}
@Get('stats/resolution-time')
@Roles(UserRoleEnum.ADMIN, UserRoleEnum.MODERATOR)
@HttpCode(HttpStatus.OK)
@ApiOperation({
summary: 'Get resolution time statistics',
description:
'Retrieves resolution time statistics by status including average, minimum, and maximum resolution times. Only accessible by ADMIN and MODERATOR roles.',
})
@ApiResponse({
status: 200,
description: 'Resolution time statistics retrieved successfully',
schema: {
type: 'array',
items: {
type: 'object',
properties: {
status: { type: 'string' },
avgResolutionTime: { type: 'number' },
minResolutionTime: { type: 'number' },
maxResolutionTime: { type: 'number' },
},
},
},
})
@ApiResponse({
status: 401,
description: 'Unauthorized - Invalid or missing JWT token',
})
@ApiResponse({
status: 403,
description: 'Forbidden - Insufficient permissions',
})
async getResolutionTimeStats() {
return this.dashboardService.getResolutionTimeStats();
}
@Get('stats/locations')
@Roles(UserRoleEnum.ADMIN, UserRoleEnum.MODERATOR)
@HttpCode(HttpStatus.OK)
@ApiOperation({
summary: 'Get location-based statistics',
description:
'Retrieves occurrence statistics grouped by location. Shows the top 10 locations with the most occurrences. Only accessible by ADMIN and MODERATOR roles.',
})
@ApiResponse({
status: 200,
description: 'Location statistics retrieved successfully',
schema: {
type: 'array',
items: {
type: 'object',
properties: {
location: { type: 'string' },
count: { type: 'number' },
},
},
},
})
@ApiResponse({
status: 401,
description: 'Unauthorized - Invalid or missing JWT token',
})
@ApiResponse({
status: 403,
description: 'Forbidden - Insufficient permissions',
})
async getLocationStats() {
return this.dashboardService.getLocationStats();
}
@Get('summary')
@Roles(UserRoleEnum.ADMIN, UserRoleEnum.MODERATOR, UserRoleEnum.USER)
@HttpCode(HttpStatus.OK)
@ApiOperation({
summary: 'Get basic dashboard summary',
description:
'Retrieves basic dashboard summary accessible to all authenticated users. Provides high-level statistics without sensitive details.',
})
@ApiResponse({
status: 200,
description: 'Dashboard summary retrieved successfully',
schema: {
type: 'object',
properties: {
totalUsers: { type: 'number' },
totalOccurrences: { type: 'number' },
openOccurrences: { type: 'number' },
resolvedOccurrences: { type: 'number' },
totalComments: { type: 'number' },
totalAttachments: { type: 'number' },
lastUpdated: { type: 'string', format: 'date-time' },
},
},
})
@ApiResponse({
status: 401,
description: 'Unauthorized - Invalid or missing JWT token',
})
async getDashboardSummary() {
const stats = await this.dashboardService.getDashboardStats();
return {
totalUsers: stats.users.total,
totalOccurrences: stats.occurrences.total,
openOccurrences:
stats.occurrences.byStatus.OPEN +
stats.occurrences.byStatus.IN_PROGRESS,
resolvedOccurrences:
stats.occurrences.byStatus.RESOLVED + stats.occurrences.byStatus.CLOSED,
totalComments: stats.activity.totalComments,
totalAttachments: stats.activity.totalAttachments,
lastUpdated: stats.lastUpdated,
};
}
}
import { Module } from '@nestjs/common';
import { DashboardController } from './dashboard.controller';
import { DashboardService } from './dashboard.service';
import { DrizzleService } from '../../common/drizzle.service';
@Module({
controllers: [DashboardController],
providers: [DashboardService, DrizzleService],
exports: [DashboardService],
})
export class DashboardModule {}
import { ApiProperty } from '@nestjs/swagger';
export class UserStatsDto {
@ApiProperty({ description: 'Total number of users' })
total: number;
@ApiProperty({ description: 'Number of users by role' })
byRole: {
USER: number;
MODERATOR: number;
ADMIN: number;
};
@ApiProperty({ description: 'Users created in the last 30 days' })
recentUsers: number;
}
export class OccurrenceStatsDto {
@ApiProperty({ description: 'Total number of occurrences' })
total: number;
@ApiProperty({ description: 'Number of occurrences by status' })
byStatus: {
OPEN: number;
IN_PROGRESS: number;
RESOLVED: number;
PAUSED: number;
CLOSED: number;
PARCIAL_RESOLVED: number;
CANCELLED: number;
};
@ApiProperty({ description: 'Number of occurrences by priority' })
byPriority: {
LOW: number;
MEDIUM: number;
HIGH: number;
URGENT: number;
};
@ApiProperty({ description: 'Occurrences created in the last 30 days' })
recentOccurrences: number;
@ApiProperty({ description: 'Average resolution time in days' })
averageResolutionTime: number;
@ApiProperty({ description: 'Occurrences with conclusions' })
withConclusions: number;
@ApiProperty({ description: 'Occurrences with attachments' })
withAttachments: number;
}
export class ActivityStatsDto {
@ApiProperty({ description: 'Total number of comments' })
totalComments: number;
@ApiProperty({ description: 'Total number of attachments' })
totalAttachments: number;
@ApiProperty({ description: 'Total number of conclusions' })
totalConclusions: number;
@ApiProperty({ description: 'Total number of assistants' })
totalAssistants: number;
@ApiProperty({ description: 'Total number of occurrence logs' })
totalLogs: number;
@ApiProperty({ description: 'Activity in the last 30 days' })
recentActivity: {
comments: number;
attachments: number;
conclusions: number;
logs: number;
};
}
export class TopPerformersDto {
@ApiProperty({ description: 'Top users by reported occurrences' })
topReporters: Array<{
userId: string;
firstName: string;
lastName: string;
count: number;
}>;
@ApiProperty({ description: 'Top users by assigned occurrences' })
topAssignees: Array<{
userId: string;
firstName: string;
lastName: string;
count: number;
}>;
@ApiProperty({ description: 'Top users by comments' })
topCommenters: Array<{
userId: string;
firstName: string;
lastName: string;
count: number;
}>;
}
export class CategoryStatsDto {
@ApiProperty({ description: 'Occurrences by category' })
byCategory: Array<{
category: string;
count: number;
percentage: number;
}>;
}
export class TimeSeriesDataDto {
@ApiProperty({
description: 'Occurrences created over time (last 12 months)',
})
occurrencesOverTime: Array<{
month: string;
count: number;
}>;
@ApiProperty({ description: 'Users registered over time (last 12 months)' })
usersOverTime: Array<{
month: string;
count: number;
}>;
}
export class DashboardStatsDto {
@ApiProperty({ description: 'User statistics' })
users: UserStatsDto;
@ApiProperty({ description: 'Occurrence statistics' })
occurrences: OccurrenceStatsDto;
@ApiProperty({ description: 'Activity statistics' })
activity: ActivityStatsDto;
@ApiProperty({ description: 'Top performers' })
topPerformers: TopPerformersDto;
@ApiProperty({ description: 'Category statistics' })
categories: CategoryStatsDto;
@ApiProperty({ description: 'Time series data' })
timeSeries: TimeSeriesDataDto;
@ApiProperty({ description: 'Last updated timestamp' })
lastUpdated: Date;
}
export * from './dashboard.module';
export * from './dashboard.controller';
export * from './dashboard.service';
export * from './dto/dashboard-stats.dto';
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment