import { Body, Controller, Delete, ForbiddenException, Get, Param, Patch, Post, Put, Req, } from '@nestjs/common'; import { Request } from 'express'; import { DashboardService } from './dashboard.service'; import { CreateSearchProviderDto } from './dto/create-search-provider.dto'; import { CreateWidgetDto } from './dto/create-widget.dto'; import { SaveLayoutDto } from './dto/save-layout.dto'; import { UpdateWidgetConfigDto } from './dto/update-widget-config.dto'; /** * REST controller for dashboard layout and widget instance management. * * All endpoints require JWT auth (global JwtAuthGuard). * Every handler extracts userId and tenantId from the request * and scopes all operations to the calling user (T-05-01, T-05-02). * * Routes: * - GET /dashboard/layout — get user's saved layout * - PUT /dashboard/layout — upsert user's layout * - GET /dashboard/widgets — list user's widget instances * - POST /dashboard/widgets — create a new widget instance * - PATCH /dashboard/widgets/:id/config — update widget config * - DELETE /dashboard/widgets/:id — remove a widget instance * - GET /dashboard/search-providers — list default + user's custom providers * - POST /dashboard/search-providers — create a custom search provider * - DELETE /dashboard/search-providers/:id — remove a custom provider */ @Controller('dashboard') export class DashboardController { constructor(private readonly dashboardService: DashboardService) {} private extractContext(req: Request) { const userId = (req as any).user?.id; const tenantId = (req as any).tenantId ?? (req as any).user?.tenantId; if (!tenantId) { throw new ForbiddenException('No tenant context'); } if (!userId) { throw new ForbiddenException('No user context'); } return { userId, tenantId }; } @Get('layout') async getLayout(@Req() req: Request) { const { userId } = this.extractContext(req); return this.dashboardService.getLayout(userId); } @Put('layout') async saveLayout(@Req() req: Request, @Body() dto: SaveLayoutDto) { const { userId, tenantId } = this.extractContext(req); return this.dashboardService.saveLayout(userId, tenantId, dto); } @Get('widgets') async getWidgets(@Req() req: Request) { const { userId } = this.extractContext(req); return this.dashboardService.getWidgets(userId); } @Post('widgets') async addWidget(@Req() req: Request, @Body() dto: CreateWidgetDto) { const { userId, tenantId } = this.extractContext(req); return this.dashboardService.addWidget(userId, tenantId, dto); } @Patch('widgets/:id/config') async updateWidgetConfig( @Param('id') id: string, @Req() req: Request, @Body() dto: UpdateWidgetConfigDto, ) { const { userId } = this.extractContext(req); return this.dashboardService.updateWidgetConfig(id, userId, dto); } @Delete('widgets/:id') async removeWidget( @Param('id') id: string, @Req() req: Request, ) { const { userId } = this.extractContext(req); return this.dashboardService.removeWidget(id, userId); } // --- Search Providers (05-02, D-15) --- @Get('search-providers') async getSearchProviders(@Req() req: Request) { const { userId } = this.extractContext(req); return this.dashboardService.getSearchProviders(userId); } @Post('search-providers') async addSearchProvider( @Req() req: Request, @Body() dto: CreateSearchProviderDto, ) { const { userId, tenantId } = this.extractContext(req); return this.dashboardService.addSearchProvider(userId, tenantId, dto); } @Delete('search-providers/:id') async removeSearchProvider( @Param('id') id: string, @Req() req: Request, ) { const { userId } = this.extractContext(req); return this.dashboardService.removeSearchProvider(id, userId); } }