Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
388 changes: 347 additions & 41 deletions backend/package-lock.json

Large diffs are not rendered by default.

18 changes: 14 additions & 4 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,34 +21,41 @@
},
"dependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/config": "^3.0.0",
"@nestjs/config": "^3.3.0",
"@nestjs/core": "^10.0.0",
"@nestjs/event-emitter": "^3.0.1",
"@nestjs/jwt": "^11.0.0",
"@nestjs/jwt": "^11.0.2",
"@nestjs/mapped-types": "*",
"@nestjs/passport": "^11.0.5",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/schedule": "^6.0.1",
"@nestjs/swagger": "^7.3.0",
"@nestjs/throttler": "^6.5.0",
"@nestjs/typeorm": "^10.0.2",
"@types/multer": "^2.0.0",
"@types/speakeasy": "^2.0.10",
"@types/uuid": "^10.0.0",
"bcryptjs": "^3.0.2",
"axios": "^1.6.0",
"bcryptjs": "^3.0.3",
"bwip-js": "^4.7.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"class-validator": "^0.14.3",
"date-fns": "^4.1.0",
"json2csv": "^6.0.0-alpha.2",
"multer": "^2.0.2",
"nestjs-i18n": "^10.5.1",
"otplib": "^13.1.1",
"papaparse": "^5.5.3",
"passport": "^0.7.0",
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0",
"pdfkit": "^0.17.2",
"pg": "^8.11.3",
"qrcode": "^1.5.4",
"redis": "^5.10.0",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1",
"speakeasy": "^2.0.0",
"swagger-ui-express": "^5.0.1",
"typeorm": "^0.3.27"
},
Expand All @@ -60,9 +67,12 @@
"@types/express": "^5.0.0",
"@types/jest": "^29.5.2",
"@types/json2csv": "^5.0.7",
"@types/jsonwebtoken": "^9.0.10",
"@types/node": "^20.3.1",
"@types/otplib": "^7.0.0",
"@types/papaparse": "^5.3.16",
"@types/passport-jwt": "^4.0.1",
"@types/passport-local": "^1.0.38",
"@types/pdfkit": "^0.17.3",
"@types/pg": "^8.10.0",
"@types/supertest": "^6.0.0",
Expand Down
122 changes: 122 additions & 0 deletions backend/src/analytics/analytics.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { Controller, Get, Query, Param, UseGuards, Req } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiBearerAuth, ApiResponse } from '@nestjs/swagger';
import { AnalyticsService } from './analytics.service';
import { AnalyticsQueryDto, DashboardStatsResponse, TrendsResponse, DistributionResponse } from './dto/analytics-query.dto';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { RbacGuard } from '../auth/guards/rbac.guard';
import { CustomThrottlerGuard } from '../security/throttler.guard';
import { RequirePermission } from '../common/decorators/require-permission.decorator';

@ApiTags('Analytics')
@ApiBearerAuth('JWT-auth')
@UseGuards(JwtAuthGuard, RbacGuard, CustomThrottlerGuard)
@Controller('api/v1/analytics')
export class AnalyticsController {
constructor(private readonly analyticsService: AnalyticsService) {}

@Get('dashboard')
@ApiOperation({ summary: 'Get main dashboard statistics' })
@RequirePermission('analytics', 'read')
async getDashboardStats(@Query() query: AnalyticsQueryDto, @Req() req: any): Promise<DashboardStatsResponse> {
return this.analyticsService.getDashboardStats(query, req.user);
}

@Get('asset-stats')
@ApiOperation({ summary: 'Get asset-specific statistics' })
@RequirePermission('analytics', 'read')
async getAssetStats() {
return this.analyticsService.getAssetStats();
}

@Get('trends')
@ApiOperation({ summary: 'Get trend data for charts' })
@RequirePermission('analytics', 'read')
async getTrends(@Query() query: AnalyticsQueryDto, @Req() req: any): Promise<TrendsResponse> {
return this.analyticsService.getTrends(query, req.user);
}

@Get('distribution')
@ApiOperation({ summary: 'Get asset distribution data' })
@RequirePermission('analytics', 'read')
async getDistribution(@Query() query: AnalyticsQueryDto, @Req() req: any): Promise<DistributionResponse> {
return this.analyticsService.getDistribution(query, req.user);
}

@Get('top-assets')
@ApiOperation({ summary: 'Get most expensive/valuable assets' })
@RequirePermission('analytics', 'read')
async getTopAssets() {
return this.analyticsService.getTopAssets();
}

@Get('alerts')
@ApiOperation({ summary: 'Get assets requiring attention' })
@RequirePermission('analytics', 'read')
async getAlerts() {
return this.analyticsService.getAlerts();
}

@Get('departments/comparison')
@ApiOperation({ summary: 'Compare departments' })
@RequirePermission('analytics', 'read')
async compareDepartments() {
// Basic implementation using existing logic
return this.analyticsService.getDistribution({}, { role: 'ADMIN' });
}

@Get('departments/:id')
@ApiOperation({ summary: 'Get department-specific analytics' })
@RequirePermission('analytics', 'read')
async getDepartmentAnalytics(@Param('id') id: string) {
return this.analyticsService.getDepartmentAnalytics(id);
}

@Get('locations/utilization')
@ApiOperation({ summary: 'Get location utilization rates' })
@RequirePermission('analytics', 'read')
async getLocationUtilization() {
return this.analyticsService.getDistribution({}, { role: 'ADMIN' });
}

@Get('locations/:id')
@ApiOperation({ summary: 'Get location-specific analytics' })
@RequirePermission('analytics', 'read')
async getLocationAnalytics(@Param('id') id: string) {
return this.analyticsService.getLocationAnalytics(id);
}

@Get('users/activity')
@ApiOperation({ summary: 'Get user activity statistics' })
@RequirePermission('analytics', 'read')
async getUserActivity() {
// Placeholder
return { activity: [] };
}

@Get('users/:id')
@ApiOperation({ summary: "Get user's asset assignment history" })
@RequirePermission('analytics', 'read')
async getUserAnalytics(@Param('id') id: string) {
return this.analyticsService.getUserAnalytics(id);
}

@Get('timeline')
@ApiOperation({ summary: 'Asset registrations over time' })
@RequirePermission('analytics', 'read')
async getTimeline(@Query() query: AnalyticsQueryDto, @Req() req: any) {
return this.analyticsService.getTrends(query, req.user);
}

@Get('forecast')
@ApiOperation({ summary: 'Predictive analytics (asset needs)' })
@RequirePermission('analytics', 'read')
async getForecast() {
// Mock forecast implementation
return {
forecast: [
{ month: '2025-02', predictedNeed: 15, confidence: 0.85 },
{ month: '2025-03', predictedNeed: 22, confidence: 0.78 },
]
};
}
}
19 changes: 19 additions & 0 deletions backend/src/analytics/analytics.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AnalyticsService } from './analytics.service';
import { AnalyticsController } from './analytics.controller';
import { Asset } from '../assets/entities/asset.entity';
import { AssetCategory } from '../asset-categories/asset-category.entity';
import { Department } from '../departments/entities/department.entity';
import { User } from '../users/entities/user.entity';
import { RedisService } from '../common/redis.service';

@Module({
imports: [
TypeOrmModule.forFeature([Asset, AssetCategory, Department, User]),
],
controllers: [AnalyticsController],
providers: [AnalyticsService, RedisService],
exports: [AnalyticsService],
})
export class AnalyticsModule {}
Loading
Loading