Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | 12x 12x 12x 12x 12x 12x 12x 12x 12x 21x 12x 1x 12x 1x 1x 12x 12x 12x 1x 12x 12x 12x 12x 1x | import {
Controller,
Get,
Post,
Param,
Query,
Body,
Req,
UseGuards,
ParseIntPipe,
DefaultValuePipe,
HttpCode,
} from '@nestjs/common';
import { AnalyticsService } from './analytics.service';
import { JwtAuthGuard } from '@app/common/guards/jwt-auth.guard';
import { RolesGuard } from '@app/common/guards/roles.guard';
import { Roles } from '@app/common/decorators/roles.decorator';
import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';
@ApiTags('Analytics')
@Controller('analytics')
export class AnalyticsController {
constructor(private readonly analyticsService: AnalyticsService) {}
@Post('listings/:id/views')
@HttpCode(201)
@ApiOperation({ summary: 'Track a listing view (public endpoint)' })
trackView(
@Param('id', ParseIntPipe) id: number,
@Body() dto: { source?: string; referrer?: string },
) {
return this.analyticsService.trackView(id, dto.source, dto.referrer);
}
// Alias endpoint for test compatibility
@Post('listings/:id/view')
@HttpCode(201)
@ApiOperation({ summary: 'Track a listing view (public endpoint)' })
trackViewAlias(
@Param('id', ParseIntPipe) id: number,
@Body() dto: { source?: string; referrer?: string },
) {
return this.analyticsService.trackView(id, dto.source, dto.referrer);
}
@Post('listings/:id/click')
@HttpCode(200)
@ApiOperation({ summary: 'Track a listing click (public endpoint)' })
trackClick(
@Param('id', ParseIntPipe) id: number,
@Body() dto: { clickType: 'phone' | 'website' | 'email' },
@Req() req?: any,
) {
const userId = req?.user?.id;
return this.analyticsService.trackClick(id, dto.clickType, userId);
}
@Get('listings/:id')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@ApiOperation({ summary: 'Get listing analytics (owner/agent only)' })
getListingAnalytics(@Param('id', ParseIntPipe) id: number, @Req() req: any) {
return this.analyticsService.getListingAnalytics(id, req.user.id);
}
@Get('popular-searches')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('ADMIN', 'MODERATOR')
@ApiBearerAuth()
@ApiOperation({ summary: 'Get popular searches (admin only)' })
getPopularSearches(
@Query('period', new DefaultValuePipe('week')) period: 'day' | 'week' | 'month',
) {
return this.analyticsService.getPopularSearches(period);
}
@Get('cohorts')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('ADMIN', 'MODERATOR')
@ApiBearerAuth()
@ApiOperation({ summary: 'Cohort analysis (admin only)' })
getCohorts(
@Query('start') start?: string,
@Query('end') end?: string,
) {
return this.analyticsService.getCohortAnalysis(start ? new Date(start) : undefined, end ? new Date(end) : undefined);
}
@Get('attribution')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('ADMIN', 'MODERATOR')
@ApiBearerAuth()
@ApiOperation({ summary: 'Channel attribution (admin only)' })
getAttribution(
@Query('period', new DefaultValuePipe('week')) period: 'day' | 'week' | 'month',
) {
return this.analyticsService.getChannelAttribution(period);
}
@Get('geo-heatmap')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('ADMIN', 'MODERATOR')
@ApiBearerAuth()
@ApiOperation({ summary: 'Geo heatmap of activity (admin only)' })
getGeoHeatmap(
@Query('period', new DefaultValuePipe('week')) period: 'day' | 'week' | 'month',
) {
return this.analyticsService.getGeoHeatmap(period);
}
@Get('dashboard/agent')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@ApiOperation({ summary: 'Get agent dashboard data' })
getAgentDashboard(@Req() req: any) {
return this.analyticsService.getAgentDashboard(req.user.id);
}
}
|