diff --git a/backend/src/iac/backend-stack.ts b/backend/src/iac/backend-stack.ts index 1d43cdfd..b1244efe 100644 --- a/backend/src/iac/backend-stack.ts +++ b/backend/src/iac/backend-stack.ts @@ -400,6 +400,9 @@ export class BackendStack extends cdk.Stack { // Create the 'status' resource under ':id' const documentProcessorResource = apiResource.addResource('document-processor'); const processFileResource = documentProcessorResource.addResource('process-file'); + // Add report-status/:reportId resource under document-processor + const reportStatusDPResource = documentProcessorResource.addResource('report-status'); + const reportStatusDPIdResource = reportStatusDPResource.addResource('{reportId}'); // Define integration options once for reuse const integrationOptions = { @@ -467,6 +470,19 @@ export class BackendStack extends cdk.Stack { options: integrationOptions, }); + // Integration for document-processor/report-status/{reportId} + const getReportStatusDPIntegration = new apigateway.Integration({ + type: apigateway.IntegrationType.HTTP_PROXY, + integrationHttpMethod: 'GET', + uri: `${serviceUrl}/api/document-processor/report-status/{reportId}`, + options: { + ...integrationOptions, + requestParameters: { + 'integration.request.path.reportId': 'method.request.path.reportId', + }, + }, + }); + // Define method options with authorization const methodOptions = { authorizer: authorizer, @@ -495,6 +511,13 @@ export class BackendStack extends cdk.Stack { // Add POST method to process file processFileResource.addMethod('POST', processFileIntegration, methodOptions); + // Add GET method to document-processor/report-status/{reportId} + reportStatusDPIdResource.addMethod('GET', getReportStatusDPIntegration, { + ...methodOptions, + requestParameters: { + 'method.request.path.reportId': true, + }, + }); // Add CORS to each resource separately - after methods have been created const corsOptions = { @@ -534,6 +557,11 @@ export class BackendStack extends cdk.Stack { ...corsOptions, allowCredentials: false, }); + // Add CORS to new resource + reportStatusDPIdResource.addCorsPreflight({ + ...corsOptions, + allowCredentials: false, + }); // Configure Gateway Responses to add CORS headers to error responses const gatewayResponseTypes = [ diff --git a/frontend/src/common/components/Icon/Icon.tsx b/frontend/src/common/components/Icon/Icon.tsx index 1bebab99..ef2dce81 100644 --- a/frontend/src/common/components/Icon/Icon.tsx +++ b/frontend/src/common/components/Icon/Icon.tsx @@ -30,13 +30,18 @@ import { faComment, faUserCircle, faGlobe as faGoogle, - faA as faApple + faA as faApple, + faFlag, + faFlask, + faChevronUp, + faChevronDown, + faVial, } from '@fortawesome/free-solid-svg-icons'; import { faFileLines as faRegularFileLines, faComment as faRegularComment, faUser as faRegularUser, - faBookmark as faRegularBookmark + faBookmark as faRegularBookmark, } from '@fortawesome/free-regular-svg-icons'; import classNames from 'classnames'; @@ -76,7 +81,12 @@ export type IconName = | 'userGear' | 'xmark' | 'google' - | 'apple'; + | 'apple' + | 'flag' + | 'flask' + | 'chevronUp' + | 'chevronDown' + | 'vial'; /** * Properties for the `Icon` component. @@ -123,6 +133,11 @@ const solidIcons: Record = { xmark: faXmark, google: faGoogle, apple: faApple, + flag: faFlag, + flask: faFlask, + chevronUp: faChevronUp, + chevronDown: faChevronDown, + vial: faVial, }; /** @@ -154,9 +169,8 @@ const Icon = ({ ...iconProps }: IconProps): JSX.Element => { // Select icon based on style - const faIcon = iconStyle === 'regular' && regularIcons[icon] - ? regularIcons[icon] - : solidIcons[icon]; + const faIcon = + iconStyle === 'regular' && regularIcons[icon] ? regularIcons[icon] : solidIcons[icon]; return ( diff --git a/frontend/src/common/components/Router/TabNavigation.tsx b/frontend/src/common/components/Router/TabNavigation.tsx index 27989d83..4113edc9 100644 --- a/frontend/src/common/components/Router/TabNavigation.tsx +++ b/frontend/src/common/components/Router/TabNavigation.tsx @@ -16,8 +16,8 @@ import ProfilePage from 'pages/Account/components/Profile/ProfilePage'; import DiagnosticsPage from 'pages/Account/components/Diagnostics/DiagnosticsPage'; import ChatPage from 'pages/Chat/ChatPage'; import UploadPage from 'pages/Upload/UploadPage'; -import ReportDetailPage from 'pages/Reports/ReportDetailPage'; import ReportsListPage from 'pages/Reports/ReportsListPage'; +import ReportDetailPage from 'pages/Reports/ReportDetailPage'; import Processing from 'pages/Processing/Processing'; /** @@ -101,14 +101,13 @@ const TabNavigation = (): JSX.Element => { - + - + { - const { t } = useTranslation('report'); - const { reportId } = useParams(); - const [selectedSegment, setSelectedSegment] = useState<'aiInsights' | 'testResults'>('aiInsights'); - const [showBookmarkToast, setShowBookmarkToast] = useState(false); - const [bookmarkToastMessage, setBookmarkToastMessage] = useState(''); - const [flaggedValuesExpanded, setFlaggedValuesExpanded] = useState(true); - const queryClient = useQueryClient(); - - // Fetch the specific report by ID - const { data: report, isLoading, isError } = useQuery({ - queryKey: ['report', reportId], - queryFn: () => fetchReportById(reportId), - enabled: !!reportId - }); +// const { reportId } = useParams<{ reportId: string }>(); + const history = useHistory(); - // Toggle bookmark mutation - const { mutate: toggleBookmark } = useMutation({ - mutationFn: ({ reportId, isBookmarked }: { reportId: string; isBookmarked: boolean }) => { - return toggleReportBookmark(reportId, isBookmarked); - }, - onSuccess: (updatedReport) => { - // Update the reports query cache - queryClient.setQueryData(['report', reportId], updatedReport); - - // Also update the reports list if it exists in the cache - queryClient.setQueryData(['reports'], (oldData: MedicalReport[] | undefined) => { - if (!oldData) return []; - return oldData.map(oldReport => - oldReport.id === updatedReport.id ? updatedReport : oldReport - ); - }); + // State to track expanded sections + const [flaggedValuesExpanded, setFlaggedValuesExpanded] = useState(true); + const [normalValuesExpanded, setNormalValuesExpanded] = useState(true); + const [activeTab, setActiveTab] = useState<'ai' | 'original'>('ai'); - // Show toast notification - setBookmarkToastMessage(updatedReport.bookmarked ? - t('detail.bookmarkAdded') : - t('detail.bookmarkRemoved') - ); - setShowBookmarkToast(true); - } - }); + // Toggle expanded state of sections + const toggleFlaggedValues = () => setFlaggedValuesExpanded(!flaggedValuesExpanded); + const toggleNormalValues = () => setNormalValuesExpanded(!normalValuesExpanded); - // Handle bookmark click - const handleBookmarkClick = () => { - if (report) { - toggleBookmark({ - reportId: report.id, - isBookmarked: !report.bookmarked - }); - } + // Handle tab selection + const handleTabChange = (tab: 'ai' | 'original') => { + setActiveTab(tab); }; - // Mock blood test data based on the screenshot - const bloodTestData: BloodTestData = { - results: [ - { name: t('detail.hemoglobin'), value: '10.1 g/dL', range: '12-15.5 mg/dL', isOutOfRange: true }, - { name: t('detail.ldl'), value: '165 mg/dL', range: '< 130 mg/dL', isOutOfRange: true }, - { name: t('detail.glucose'), value: '110 mg/dL', range: '70 - 99 mg/dL', isOutOfRange: true }, - { name: t('detail.alt'), value: '12.5 g/dL', range: '7-35 U/L' }, - { name: t('detail.wbc'), value: '6,800 /μL', range: '4-10×10⁹/L' }, - { name: t('detail.vitaminD'), value: '35 ng/dL', range: '30-50 mg/dL' }, - { name: t('detail.cholesterol'), value: '210 mg/dL', range: '> 200 mg/dL', isOutOfRange: true } - ], - comments: t('detail.hemoglobinComment') + // Handle close button + const handleClose = () => { + history.goBack(); }; - // Mock flagged values data for AI insights - const flaggedValues: FlaggedValue[] = [ - { - id: 'ldl', - title: t('detail.highLdl'), - value: '165', - units: 'mg/dL', - severity: 'High', - conclusion: t('detail.ldlConclusion'), - suggestions: [ - t('detail.ldlSuggestion1'), - t('detail.ldlSuggestion2'), - t('detail.ldlSuggestion3') - ] - }, - { - id: 'hemoglobin', - title: t('detail.lowHemoglobin'), - value: '10.1', - units: 'g/dL', - severity: 'Low', - conclusion: t('detail.hemoglobinConclusion'), - suggestions: [ - t('detail.hemoglobinSuggestion1'), - t('detail.hemoglobinSuggestion2') - ] - } - ]; + // Handle action buttons + const handleDiscard = () => { + history.goBack(); + }; - // Handle segment change - const handleSegmentChange = (e: CustomEvent) => { - setSelectedSegment(e.detail.value); + const handleNewUpload = () => { + history.push('/tabs/upload'); }; - // Toggle flagged values section - const toggleFlaggedValues = () => { - setFlaggedValuesExpanded(!flaggedValuesExpanded); + // Hardcoded data for now, will be replaced with real API data later + const reportData = { + title: 'Blood Test', + category: 'General', + flaggedValues: [ + { + name: 'High LDL Cholesterol', + level: 'High', + value: '165 mg/dL', + conclusion: 'Elevated LDL (bad cholesterol) increases your risk of cardiovascular disease', + suggestions: [ + 'Consider a heart-healthy diet (e.g., Mediterranean).', + 'Increase physical activity.', + 'Visit the nearest emergency room.', + ], + }, + { + name: 'Low Hemoglobin (10.1 g/dL)', + level: 'Low', + value: '10.1 g/dL', + conclusion: 'This level suggests anemia, which may cause fatigue and weakness.', + suggestions: [ + 'Test for iron, B12, and folate deficiency.', + 'Consider iron-rich foods or supplements after medical consultation.already on one.', + ], + }, + ], + normalValues: [ + { + name: 'White Blood Cell Count', + value: '6,800 /µL', + conclusion: 'Normal WBC count; your immune system is functioning well', + suggestions: ['Keep up a balanced diet, manage stress, and get adequate rest.'], + }, + { + name: 'Vitamin D', + value: '35 ng/mL', + conclusion: 'Adequate levels for bone health and immunity.', + suggestions: ['Maintain outdoor exposure and dietary intake.'], + }, + ], + hasEmergency: true, }; - if (isLoading || !report) { - return ( - - -
-
-
- -
- {t('detail.loading')} + return ( + + + {/* Header section */} +
+
+

Results Analysis

+
+ + +
- - - ); - } - if (isError) { - return ( - - -
-
-
- -
- {t('detail.errorLoading')} + {/* Category & Title */} +
+ {reportData.category} +
+
- - - ); - } - - return ( - - -
-
-
-
- -
-
+

{reportData.title}

+
-
{report.category}
+ {/* Tab selector for AI Insights vs Original Report */} +
+
handleTabChange('ai')} + > + + + + + + AI Insights +
+
handleTabChange('original')} + > + Original Report +
+
-
-

{report.title}

- +
+ - - + + + +
+

+ Please contact your doctor or seek emergency care immediately. +

-
+ )} - - - - {t('detail.aiInsights')} - - - {t('detail.testResults')} - - + {/* Flagged values section */} +
+
+
+ +
+

Flagged values

+
+ +
+
- - {selectedSegment === 'aiInsights' && ( -
-
- -

{t('detail.emergencyWarning')}

+ {flaggedValuesExpanded && + reportData.flaggedValues.map((item, index) => ( +
+
+
{item.name}
+
+ {item.level} +
+
{item.value}
- -
-
-
- -

{t('detail.flaggedValues')}

-
- +
+
+

Conclusion:

+

{item.conclusion}

- - {flaggedValuesExpanded && ( -
- {flaggedValues.map((value) => ( -
-
- {value.title} -
- {value.severity} -
- {value.value} {value.units} -
-
-
-

{t('detail.conclusion')}

-

{value.conclusion}

-
-
-

{t('detail.suggestions')}

-
    - {value.suggestions.map((suggestion, index) => ( -
  • {suggestion}
  • - ))} -
-
-
-
+
+

Suggestions:

+
    + {item.suggestions.map((suggestion, idx) => ( +
  • {suggestion}
  • ))} -
- )} + +
- )} + ))} +
- {selectedSegment === 'testResults' && ( -
- - - - - - - - - - {bloodTestData.results.map((result, index) => ( - - - - - - ))} - -
{t('detail.test')}{t('detail.results')}{t('detail.refRange')}
{result.name} - {result.value} - {result.range}
+ {/* Normal values section */} +
+
+
+ +
+

Normal values

+
+ +
+
- {bloodTestData.comments && ( -
-

{t('detail.medicalComments')}

-

{bloodTestData.comments}

+ {normalValuesExpanded && + reportData.normalValues.map((item, index) => ( +
+
+
{item.name}
+
{item.value}
+
+
+
+

Conclusion:

+

{item.conclusion}

+
+
+

Suggestions:

+
    + {item.suggestions.map((suggestion, idx) => ( +
  • {suggestion}
  • + ))} +
- )} +
- )} - - + ))} +
+ + {/* Doctor information note */} +
+
+ +
+
+ With all interpretations, these results should be discussed with your doctor. +
+
- setShowBookmarkToast(false)} - message={bookmarkToastMessage} - duration={2000} - position="bottom" - color="primary" - /> + {/* AI Assistant help section */} +
+
+ Still need further clarifications? +
+
Ask our AI Assistant >
+
+ + {/* Action buttons at the bottom */} +
+ + +
); }; -export default ReportDetailPage; +export default ReportDetailPage; \ No newline at end of file