Files
CRM_Montzar/apps/api/src/reports/reports.controller.ts
T
Mahdi 7ae6a5837a feat: enforce role section permissions and add a leads permission
Unticking a section hides it from the menu, closes its routes and blocks its writes on the server; existing roles keep their current access via migration.
2026-10-21 13:45:16 +03:30

162 lines
3.7 KiB
TypeScript

import {
Body,
Controller,
Delete,
Get,
Param,
Patch,
Post,
Query,
UseGuards,
} from '@nestjs/common';
import {
IsBoolean,
IsObject,
IsOptional,
IsString,
MaxLength,
} from 'class-validator';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { RequirePermission, SectionPermissionGuard } from '../auth/section-permission.guard';
import { CurrentUser } from '../auth/current-user.decorator';
import type { Actor } from '../common/record-scope';
import { SavedViewsService } from '../saved-views/saved-views.service';
import { ReportsBuilderQueryDto, ReportsDrilldownQueryDto, ReportsQueryDto } from './dto';
import { ReportsService } from './reports.service';
class CreateReportViewDto {
@IsString()
@MaxLength(120)
name!: string;
@IsObject()
filters!: Record<string, unknown>;
@IsOptional()
@IsBoolean()
isShared?: boolean;
}
class UpdateReportViewDto {
@IsOptional()
@IsString()
@MaxLength(120)
name?: string;
@IsOptional()
@IsObject()
filters?: Record<string, unknown>;
@IsOptional()
@IsBoolean()
isShared?: boolean;
}
@Controller('reports')
@UseGuards(JwtAuthGuard, SectionPermissionGuard)
@RequirePermission('reports', { reads: true })
export class ReportsController {
constructor(
private readonly reportsService: ReportsService,
private readonly savedViews: SavedViewsService,
) {}
@Get('dashboard')
dashboard(@CurrentUser() user: Actor, @Query() query: ReportsQueryDto) {
return this.reportsService.dashboard(user, {
from: query.from,
to: query.to,
});
}
@Get('drilldown')
drilldown(
@CurrentUser() user: Actor,
@Query() query: ReportsDrilldownQueryDto,
) {
return this.reportsService.drilldown(user, {
kind: query.kind,
id: query.id,
from: query.from,
to: query.to,
});
}
@Post('builder')
builder(
@CurrentUser() user: Actor,
@Body() dto: ReportsBuilderQueryDto,
) {
return this.reportsService.builder(user, {
metric: dto.metric,
dimension: dto.dimension,
from: dto.from,
to: dto.to,
userId: dto.userId,
teamId: dto.teamId,
});
}
/** داشبورد صفحهٔ خانه برای همه باز است */
@Get('home')
@RequirePermission('reports', { reads: false })
home(@CurrentUser() user: Actor) {
return this.reportsService.home(user);
}
@Get('views')
listViews(@CurrentUser() user: Actor) {
return this.savedViews.list(user.organizationId, user.userId, 'reports');
}
@Get('views/:id')
getView(@CurrentUser() user: Actor, @Param('id') id: string) {
return this.savedViews.get(user.organizationId, user.userId, id, 'reports');
}
@Post('views')
createView(@CurrentUser() user: Actor, @Body() dto: CreateReportViewDto) {
return this.savedViews.create(user.organizationId, user.userId, {
name: dto.name,
scope: 'reports',
filters: dto.filters,
isShared: dto.isShared,
});
}
@Patch('views/:id')
updateView(
@CurrentUser() user: Actor,
@Param('id') id: string,
@Body() dto: UpdateReportViewDto,
) {
const isAdmin = user.role === 'owner' || user.role === 'admin';
return this.savedViews.update(
user.organizationId,
user.userId,
id,
{
name: dto.name,
filters: dto.filters,
isShared: dto.isShared,
scope: 'reports',
},
isAdmin,
'reports',
);
}
@Delete('views/:id')
async removeView(@CurrentUser() user: Actor, @Param('id') id: string) {
const isAdmin = user.role === 'owner' || user.role === 'admin';
await this.savedViews.remove(
user.organizationId,
user.userId,
id,
isAdmin,
'reports',
);
return { ok: true };
}
}