import { Body, Controller, Get, Param, ParseIntPipe, Post, Put, Query, Req, Res, UploadedFiles, UseGuards, UseInterceptors, } from '@nestjs/common'; import { FilesInterceptor } from '@nestjs/platform-express'; import { ApiBearerAuth, ApiBody, ApiConsumes, ApiOperation, ApiTags } from '@nestjs/swagger'; import { memoryStorage } from 'multer'; import type { Response } from 'express'; import { ClinicOrgGuard } from '../../common/guards/clinic-org.guard'; import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'; import { SaveTreatmentDraftDto, SaveTreatmentLabCasesDto, } from './dto/treatment.dto'; import { TreatmentsService } from './treatments.service'; @ApiTags('treatments') @ApiBearerAuth('JWT-auth') @UseGuards(JwtAuthGuard, ClinicOrgGuard) @Controller('treatments') export class TreatmentsController { constructor(private readonly treatmentsService: TreatmentsService) {} @Get('linked-organizations') @ApiOperation({ summary: 'List active linked counterpart organizations (TAB_TREATMENT_READ)' }) listLinkedOrganizations(@Req() req: { user: { id: string; organizationId?: string } }) { const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user); return this.treatmentsService.listLinkedOrganizations(req.user.id, organizationId); } @Get('patients/:patientId/history') @ApiOperation({ summary: 'List completed treatments for a patient (TAB_TREATMENT_READ)' }) listPatientHistory( @Param('patientId') patientId: string, @Query('limit', new ParseIntPipe({ optional: true })) limit = 20, @Req() req: { user: { id: string; organizationId?: string } }, ) { const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user); return this.treatmentsService.listPatientHistory( patientId, organizationId, req.user.id, limit, ); } @Get('appointments/:appointmentId/draft') @ApiOperation({ summary: 'Get draft treatment for an appointment (TAB_TREATMENT_READ)' }) getDraft( @Param('appointmentId') appointmentId: string, @Req() req: { user: { id: string; organizationId?: string } }, ) { const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user); return this.treatmentsService.getDraftForAppointment( appointmentId, organizationId, req.user.id, ); } @Put('appointments/:appointmentId/draft') @ApiOperation({ summary: 'Save draft treatment details for an appointment (TAB_TREATMENT_EDIT)' }) saveDraft( @Param('appointmentId') appointmentId: string, @Body() dto: SaveTreatmentDraftDto, @Req() req: { user: { id: string; organizationId?: string } }, ) { const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user); return this.treatmentsService.saveDraftForAppointment( appointmentId, dto, organizationId, req.user.id, ); } @Put('appointments/:appointmentId/lab-cases') @ApiOperation({ summary: 'Save lab case groupings for a draft treatment (TAB_TREATMENT_EDIT)' }) saveLabCases( @Param('appointmentId') appointmentId: string, @Body() dto: SaveTreatmentLabCasesDto, @Req() req: { user: { id: string; organizationId?: string } }, ) { const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user); return this.treatmentsService.saveLabCasesForAppointment( appointmentId, dto, organizationId, req.user.id, ); } @Post('appointments/:appointmentId/details/:detailClientKey/attachments') @ApiOperation({ summary: 'Upload attachments for a draft treatment detail (TAB_TREATMENT_EDIT)' }) @ApiConsumes('multipart/form-data') @ApiBody({ schema: { type: 'object', properties: { files: { type: 'array', items: { type: 'string', format: 'binary' }, }, }, }, }) @UseInterceptors( FilesInterceptor('files', 20, { storage: memoryStorage(), }), ) uploadDetailAttachments( @Param('appointmentId') appointmentId: string, @Param('detailClientKey') detailClientKey: string, @UploadedFiles() files: Express.Multer.File[], @Req() req: { user: { id: string; organizationId?: string } }, ) { const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user); return this.treatmentsService.uploadDetailAttachments( appointmentId, detailClientKey, files, organizationId, req.user.id, ); } /** @deprecated Use details/:detailClientKey/attachments */ @Post('appointments/:appointmentId/cases/:caseClientKey/attachments') @ApiOperation({ summary: 'Legacy alias for detail attachment upload' }) @ApiConsumes('multipart/form-data') @UseInterceptors( FilesInterceptor('files', 20, { storage: memoryStorage(), }), ) uploadDetailAttachmentsLegacy( @Param('appointmentId') appointmentId: string, @Param('caseClientKey') caseClientKey: string, @UploadedFiles() files: Express.Multer.File[], @Req() req: { user: { id: string; organizationId?: string } }, ) { const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user); return this.treatmentsService.uploadDetailAttachments( appointmentId, caseClientKey, files, organizationId, req.user.id, ); } @Get('attachments/:attachmentId/file') @ApiOperation({ summary: 'Download a treatment attachment (TAB_TREATMENT_READ)' }) async downloadAttachment( @Param('attachmentId') attachmentId: string, @Req() req: { user: { id: string; organizationId?: string } }, @Res() res: Response, ) { const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user); const file = await this.treatmentsService.streamAttachmentFile( attachmentId, organizationId, req.user.id, ); res.setHeader('Content-Type', file.mimeType); res.setHeader('Content-Disposition', `inline; filename="${file.fileName}"`); file.stream.pipe(res); } @Post('lab-cases/:labCaseId/send') @ApiOperation({ summary: 'Send a lab case to its destination organization (TAB_TREATMENT_EDIT)' }) sendLabCase( @Param('labCaseId') labCaseId: string, @Req() req: { user: { id: string; organizationId?: string } }, ) { const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user); return this.treatmentsService.sendLabCase(labCaseId, organizationId, req.user.id); } }