diff --git a/backend/src/document-processor/controllers/document-processor.controller.ts b/backend/src/document-processor/controllers/document-processor.controller.ts index e0c8ed8a..59e33249 100644 --- a/backend/src/document-processor/controllers/document-processor.controller.ts +++ b/backend/src/document-processor/controllers/document-processor.controller.ts @@ -143,21 +143,31 @@ export class DocumentProcessorController { throw new BadRequestException(`Report with ID ${reportId} has no associated file`); } - // Update report status to IN_PROGRESS before starting async processing - report.processingStatus = ProcessingStatus.IN_PROGRESS; - report.updatedAt = new Date().toISOString(); - await this.reportsService.updateReport(report); + let message = ''; - // Start async processing in background - this.processReportAsync(reportId, userId, report.filePath).catch(error => { - this.logger.error(`Async processing failed for report ${reportId}: ${error.message}`); - }); + if (report.processingStatus === ProcessingStatus.IN_PROGRESS) { + message = 'Document processing is already in progress. Please check the report status.'; + } else if (report.processingStatus === ProcessingStatus.PROCESSED) { + message = 'Document has already been processed. No further action is needed.'; + } else { + message = 'Document processing started. Check the report status to know when it completes.'; + + // Update report status to IN_PROGRESS before starting async processing + report.processingStatus = ProcessingStatus.IN_PROGRESS; + report.updatedAt = new Date().toISOString(); + await this.reportsService.updateReport(report); + + // Start async processing in background + this.processReportAsync(reportId, userId, report.filePath).catch(error => { + this.logger.error(`Async processing failed for report ${reportId}: ${error.message}`); + }); + } return { success: true, reportId: report.id, - status: ProcessingStatus.IN_PROGRESS, - message: 'Document processing started. Check the report status to know when it completes.', + status: report.processingStatus, + message, }; } catch (error: unknown) { this.logger.error( diff --git a/backend/src/iac/backend-stack.ts b/backend/src/iac/backend-stack.ts index a78436b7..b6b3aad6 100644 --- a/backend/src/iac/backend-stack.ts +++ b/backend/src/iac/backend-stack.ts @@ -454,6 +454,18 @@ export class BackendStack extends cdk.Stack { }, }); + const deleteReportIntegration = new apigateway.Integration({ + type: apigateway.IntegrationType.HTTP_PROXY, + integrationHttpMethod: 'DELETE', + uri: `${serviceUrl}/api/reports/{id}`, + options: { + ...integrationOptions, + requestParameters: { + 'integration.request.path.id': 'method.request.path.id', + }, + }, + }); + const patchReportStatusIntegration = new apigateway.Integration({ type: apigateway.IntegrationType.HTTP_PROXY, integrationHttpMethod: 'PATCH', @@ -517,6 +529,13 @@ export class BackendStack extends cdk.Stack { }, }); + reportIdResource.addMethod('DELETE', deleteReportIntegration, { + ...methodOptions, + requestParameters: { + 'method.request.path.id': true, + }, + }); + reportStatusResource.addMethod('PATCH', patchReportStatusIntegration, { ...methodOptions, requestParameters: { diff --git a/backend/src/reports/reports.controller.ts b/backend/src/reports/reports.controller.ts index 707eb403..f3c21a6f 100644 --- a/backend/src/reports/reports.controller.ts +++ b/backend/src/reports/reports.controller.ts @@ -10,6 +10,7 @@ import { UnauthorizedException, Post, NotFoundException, + Delete, } from '@nestjs/common'; import { ApiTags, @@ -192,6 +193,25 @@ export class ReportsController { return this.reportsService.saveReport(filePath, userId, originalFilename, fileSize); } + @ApiOperation({ summary: 'Delete a report' }) + @ApiResponse({ + status: 200, + description: 'Report deleted successfully', + }) + @ApiResponse({ + status: 404, + description: 'Report not found', + }) + @ApiParam({ + name: 'id', + description: 'Report ID', + }) + @Delete(':id') + async deleteReport(@Param('id') id: string, @Req() request: RequestWithUser): Promise { + const userId = this.extractUserId(request); + await this.reportsService.deleteReport(id, userId); + } + private extractUserId(request: RequestWithUser): string { // The user object is attached to the request by our middleware const user = request.user; diff --git a/frontend/src/common/utils/i18n/resources/en/common.json b/frontend/src/common/utils/i18n/resources/en/common.json index 204ff9b8..8407824e 100644 --- a/frontend/src/common/utils/i18n/resources/en/common.json +++ b/frontend/src/common/utils/i18n/resources/en/common.json @@ -80,6 +80,9 @@ "pages": { "chat": { "title": "AI Assistant" + }, + "upload": { + "title": "Upload Report" } } } diff --git a/frontend/src/pages/Reports/ReportDetailPage.tsx b/frontend/src/pages/Reports/ReportDetailPage.tsx index ed989846..c6e95ae0 100644 --- a/frontend/src/pages/Reports/ReportDetailPage.tsx +++ b/frontend/src/pages/Reports/ReportDetailPage.tsx @@ -80,12 +80,13 @@ const ReportDetailPage: React.FC = () => { // Handle close button const handleClose = () => { - history.goBack(); + history.push('/tabs/home'); }; // Handle action buttons - const handleDiscard = () => { - history.goBack(); + const handleDiscard = async () => { + await axios.delete(`${API_URL}/api/reports/${reportId}`, await getAuthConfig()); + history.push('/tabs/home'); }; const handleNewUpload = () => { diff --git a/frontend/src/pages/Reports/components/AiAnalysisTab.tsx b/frontend/src/pages/Reports/components/AiAnalysisTab.tsx index df0dd196..fe6217cc 100644 --- a/frontend/src/pages/Reports/components/AiAnalysisTab.tsx +++ b/frontend/src/pages/Reports/components/AiAnalysisTab.tsx @@ -45,11 +45,13 @@ const AiAnalysisTab: React.FC = ({ {isLowConfidence && } {/* Flagged values section */} - + {flaggedValues.length > 0 && ( + + )} {/* Normal values section */} { const { t } = useTranslation(); - const [isModalOpen, setIsModalOpen] = useState(false); + const [isModalOpen, setIsModalOpen] = useState(true); const history = useHistory(); const handleUploadComplete = () => { @@ -21,6 +21,16 @@ const UploadPage = (): JSX.Element => { history.push('/tabs/home'); }; + useEffect(() => { + // Automatically open the upload modal when the component mounts + setIsModalOpen(true); + + // Cleanup function to close the modal when the component unmounts + return () => { + setIsModalOpen(false); + }; + }, []); + return ( @@ -29,15 +39,6 @@ const UploadPage = (): JSX.Element => { -
-

{t('pages.upload.subtitle')}

-

{t('pages.upload.description')}

- - setIsModalOpen(true)}> - {t('upload.selectFile')} - -
- setIsModalOpen(false)}