diff --git a/backend/src/services/perplexity.service.ts b/backend/src/services/perplexity.service.ts index fe56901..6c7af50 100644 --- a/backend/src/services/perplexity.service.ts +++ b/backend/src/services/perplexity.service.ts @@ -2,7 +2,7 @@ import { Injectable, Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import axios from 'axios'; import { AwsSecretsService } from './aws-secrets.service'; -import { LabValue } from 'src/document-processor/services/aws-bedrock.service'; +import { LabValue } from '../document-processor/services/aws-bedrock.service'; export interface PerplexityMessage { role: 'system' | 'user' | 'assistant'; diff --git a/frontend/src/common/hooks/useChat.ts b/frontend/src/common/hooks/useChat.ts index 8505a75..6538355 100644 --- a/frontend/src/common/hooks/useChat.ts +++ b/frontend/src/common/hooks/useChat.ts @@ -1,21 +1,20 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { bedrockService } from '../services/ai/bedrock.service'; - -export const CHAT_QUERY_KEY = 'chat'; +import { QueryKey } from 'common/utils/constants'; export function useChat(sessionId?: string) { const queryClient = useQueryClient(); // Query for getting chat session const { data: session } = useQuery({ - queryKey: [CHAT_QUERY_KEY, sessionId], + queryKey: [QueryKey.Chat, sessionId], queryFn: () => (sessionId ? bedrockService.getChatSession(sessionId) : undefined), enabled: !!sessionId, }); // Query for getting all sessions const { data: sessions } = useQuery({ - queryKey: [CHAT_QUERY_KEY, 'sessions'], + queryKey: [QueryKey.Chat, 'sessions'], queryFn: () => bedrockService.getAllSessions(), }); @@ -23,7 +22,7 @@ export function useChat(sessionId?: string) { const createSession = useMutation({ mutationFn: () => bedrockService.createChatSession(), onSuccess: (newSessionId) => { - queryClient.invalidateQueries({ queryKey: [CHAT_QUERY_KEY, 'sessions'] }); + queryClient.invalidateQueries({ queryKey: [QueryKey.Chat, 'sessions'] }); return newSessionId; }, }); @@ -35,8 +34,8 @@ export function useChat(sessionId?: string) { return bedrockService.sendMessage(sessionId, message); }, onSuccess: () => { - queryClient.invalidateQueries({ queryKey: [CHAT_QUERY_KEY, sessionId] }); - queryClient.invalidateQueries({ queryKey: [CHAT_QUERY_KEY, 'sessions'] }); + queryClient.invalidateQueries({ queryKey: [QueryKey.Chat, sessionId] }); + queryClient.invalidateQueries({ queryKey: [QueryKey.Chat, 'sessions'] }); }, }); diff --git a/frontend/src/common/hooks/useReports.ts b/frontend/src/common/hooks/useReports.ts index 8d0d96b..3b5ee30 100644 --- a/frontend/src/common/hooks/useReports.ts +++ b/frontend/src/common/hooks/useReports.ts @@ -1,10 +1,12 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { fetchAllReports, fetchLatestReports, markReportAsRead } from '../api/reportService'; +import { + fetchAllReports, + fetchLatestReports, + markReportAsRead, + toggleReportBookmark, +} from '../api/reportService'; import { MedicalReport } from '../models/medicalReport'; - -// Query keys -const REPORTS_KEY = 'reports'; -const LATEST_REPORTS_KEY = 'latestReports'; +import { QueryKey } from 'common/utils/constants'; /** * Hook to fetch the latest reports. @@ -13,7 +15,7 @@ const LATEST_REPORTS_KEY = 'latestReports'; */ export const useGetLatestReports = (limit = 3) => { return useQuery({ - queryKey: [LATEST_REPORTS_KEY, limit], + queryKey: [QueryKey.LatestReports, limit], queryFn: () => fetchLatestReports(limit), refetchOnMount: true, refetchOnWindowFocus: true, @@ -27,7 +29,7 @@ export const useGetLatestReports = (limit = 3) => { */ export const useGetAllReports = () => { return useQuery({ - queryKey: [REPORTS_KEY], + queryKey: [QueryKey.Reports], queryFn: fetchAllReports, }); }; @@ -43,7 +45,7 @@ export const useMarkReportAsRead = () => { mutationFn: (reportId: string) => markReportAsRead(reportId), onSuccess: (updatedReport: MedicalReport) => { // Update the reports cache - queryClient.setQueryData([REPORTS_KEY], (oldReports) => { + queryClient.setQueryData([QueryKey.Reports], (oldReports) => { if (!oldReports) return undefined; return oldReports.map((report) => report.id === updatedReport.id ? updatedReport : report, @@ -51,7 +53,7 @@ export const useMarkReportAsRead = () => { }); // Update the latest reports cache - queryClient.setQueryData([LATEST_REPORTS_KEY], (oldReports) => { + queryClient.setQueryData([QueryKey.LatestReports], (oldReports) => { if (!oldReports) return undefined; return oldReports.map((report) => report.id === updatedReport.id ? updatedReport : report, @@ -60,3 +62,43 @@ export const useMarkReportAsRead = () => { }, }); }; + +/** + * Hook to toggle the bookmark status of a report. + * @returns Mutation result for toggling the bookmark status + */ +export const useToggleReportBookmark = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: ({ reportId, isBookmarked }: { reportId: string; isBookmarked: boolean }) => + toggleReportBookmark(reportId, isBookmarked), + onSuccess: (updatedReport: MedicalReport) => { + // Update the reports cache + queryClient.setQueryData([QueryKey.Reports], (oldReports) => { + if (!oldReports) return undefined; + return oldReports.map((report) => + report.id === updatedReport.id ? updatedReport : report, + ); + }); + + // Update the latest reports cache + queryClient.setQueryData([QueryKey.LatestReports], (oldReports) => { + if (!oldReports) return undefined; + return oldReports.map((report) => + report.id === updatedReport.id ? updatedReport : report, + ); + }); + + // Update the bookmark status in the report detail page + queryClient.setQueryData( + [QueryKey.ReportDetail, reportId], + (oldReport) => { + if (!oldReport) return undefined; + if (oldReport.id !== updatedReport.id) return oldReport; + return { ...oldReport, bookmarked: updatedReport.bookmarked }; + }, + ); + }, + }); +}; diff --git a/frontend/src/common/utils/constants.ts b/frontend/src/common/utils/constants.ts index 4b7b29f..bda8f75 100644 --- a/frontend/src/common/utils/constants.ts +++ b/frontend/src/common/utils/constants.ts @@ -9,6 +9,10 @@ export enum QueryKey { UserProfile = 'UserProfile', Users = 'Users', UserTokens = 'UserTokens', + Chat = 'Chat', + Reports = 'Reports', + LatestReports = 'LatestReports', + ReportDetail = 'ReportDetail', } /** diff --git a/frontend/src/pages/Home/HomePage.tsx b/frontend/src/pages/Home/HomePage.tsx index 8adc643..1c6a163 100644 --- a/frontend/src/pages/Home/HomePage.tsx +++ b/frontend/src/pages/Home/HomePage.tsx @@ -12,11 +12,12 @@ import { import { useTranslation } from 'react-i18next'; import { useHistory } from 'react-router-dom'; import { useState } from 'react'; -import { useGetLatestReports, useMarkReportAsRead } from 'common/hooks/useReports'; +import { + useGetLatestReports, + useMarkReportAsRead, + useToggleReportBookmark, +} from 'common/hooks/useReports'; import { useCurrentUser } from 'common/hooks/useAuth'; -import { toggleReportBookmark } from 'common/api/reportService'; -import { useQueryClient } from '@tanstack/react-query'; -import { MedicalReport } from 'common/models/medicalReport'; import Avatar from 'common/components/Icon/Avatar'; import ReportItem from './components/ReportItem/ReportItem'; import NoReportsMessage from './components/NoReportsMessage/NoReportsMessage'; @@ -30,7 +31,7 @@ import './HomePage.scss'; const HomePage: React.FC = () => { const { t } = useTranslation('home'); const history = useHistory(); - const queryClient = useQueryClient(); + const toggleBookmark = useToggleReportBookmark(); const { data: reports, isLoading, isError } = useGetLatestReports(3); const { mutate: markAsRead } = useMarkReportAsRead(); const currentUser = useCurrentUser(); @@ -47,31 +48,6 @@ const HomePage: React.FC = () => { history.push(`/tabs/reports/${reportId}`); }; - const handleToggleBookmark = async (reportId: string, isCurrentlyBookmarked: boolean) => { - try { - // Toggle the bookmark status - const updatedReport = await toggleReportBookmark(reportId, !isCurrentlyBookmarked); - - // Update the reports in the cache - queryClient.setQueryData(['reports'], (oldReports) => { - if (!oldReports) return []; - return oldReports.map((report) => - report.id === updatedReport.id ? updatedReport : report, - ); - }); - - // Update the latest reports cache with the correct query key including the limit - queryClient.setQueryData(['latestReports', 3], (oldReports) => { - if (!oldReports) return []; - return oldReports.map((report) => - report.id === updatedReport.id ? updatedReport : report, - ); - }); - } catch (error) { - console.error('Failed to toggle bookmark:', error); - } - }; - const handleUpload = () => { history.push('/upload'); }; @@ -121,7 +97,9 @@ const HomePage: React.FC = () => { key={report.id} report={report} onClick={() => handleReportClick(report.id)} - onToggleBookmark={() => handleToggleBookmark(report.id, report.bookmarked)} + onToggleBookmark={() => + toggleBookmark.mutate({ reportId: report.id, isBookmarked: report.bookmarked }) + } showBookmarkButton={true} /> )); diff --git a/frontend/src/pages/Processing/ProcessingPage.tsx b/frontend/src/pages/Processing/ProcessingPage.tsx index 27bf5c2..68ed37a 100644 --- a/frontend/src/pages/Processing/ProcessingPage.tsx +++ b/frontend/src/pages/Processing/ProcessingPage.tsx @@ -8,6 +8,8 @@ import './ProcessingPage.scss'; import { getAuthConfig } from 'common/api/reportService'; import ProcessingError from './components/ProcessingError'; import ProcessingAnimation from './components/ProcessingAnimation'; +import { QueryKey } from 'common/utils/constants'; +import { useQueryClient } from '@tanstack/react-query'; const API_URL = import.meta.env.VITE_BASE_URL_API || ''; @@ -20,6 +22,7 @@ const ProcessingPage: React.FC = () => { const firstName = currentUser?.name?.split(' ')[0]; const axios = useAxios(); const history = useHistory(); + const queryClient = useQueryClient(); // States to track processing const [isProcessing, setIsProcessing] = useState(true); @@ -70,6 +73,9 @@ const ProcessingPage: React.FC = () => { console.log('Processing complete'); + queryClient.invalidateQueries({ queryKey: [QueryKey.Reports] }); + queryClient.invalidateQueries({ queryKey: [QueryKey.LatestReports] }); + history.push(`/tabs/reports/${reportId}`); } else if (data.status === 'failed') { if (data.isMedicalReport === false) { diff --git a/frontend/src/pages/Reports/ReportDetailPage.tsx b/frontend/src/pages/Reports/ReportDetailPage.tsx index d1151f5..9b445bc 100644 --- a/frontend/src/pages/Reports/ReportDetailPage.tsx +++ b/frontend/src/pages/Reports/ReportDetailPage.tsx @@ -2,7 +2,7 @@ import { IonPage, IonContent } from '@ionic/react'; import { useState } from 'react'; import { useHistory, useParams } from 'react-router-dom'; import './ReportDetailPage.scss'; -import { useQuery } from '@tanstack/react-query'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; import axios from 'axios'; import { MedicalReport } from '../../common/models/medicalReport'; import { useTranslation } from 'react-i18next'; @@ -16,6 +16,7 @@ import InfoCard from './components/InfoCard'; import ActionButtons from './components/ActionButtons'; import AiAnalysisTab from './components/AiAnalysisTab'; import UploadModal from 'common/components/Upload/UploadModal'; +import { QueryKey } from 'common/utils/constants'; const API_URL = import.meta.env.VITE_BASE_URL_API || ''; @@ -38,6 +39,7 @@ const ReportDetailPage: React.FC = () => { const { t } = useTranslation(); const { createToast } = useToasts(); const [isUploadModalOpen, setIsUploadModalOpen] = useState(false); + const queryClient = useQueryClient(); const handleUploadComplete = () => { setIsUploadModalOpen(false); @@ -46,7 +48,7 @@ const ReportDetailPage: React.FC = () => { // Fetch report data using react-query const { data, isLoading, error } = useQuery({ - queryKey: ['report', reportId], + queryKey: [QueryKey.ReportDetail, reportId], queryFn: () => fetchReportById(reportId!), enabled: !!reportId, }); @@ -107,6 +109,10 @@ const ReportDetailPage: React.FC = () => { duration: 2000, }); + queryClient.invalidateQueries({ queryKey: [QueryKey.Reports] }); + queryClient.invalidateQueries({ queryKey: [QueryKey.LatestReports] }); + queryClient.invalidateQueries({ queryKey: [QueryKey.ReportDetail, reportId] }); + // Navigate back history.push('/tabs/home'); } catch (error) { @@ -129,6 +135,11 @@ const ReportDetailPage: React.FC = () => { setIsProcessing(true); await axios.delete(`${API_URL}/api/reports/${reportId}`, await getAuthConfig()); setIsProcessing(false); + + queryClient.invalidateQueries({ queryKey: [QueryKey.Reports] }); + queryClient.invalidateQueries({ queryKey: [QueryKey.LatestReports] }); + queryClient.invalidateQueries({ queryKey: [QueryKey.ReportDetail, reportId] }); + setIsUploadModalOpen(true); } catch (error) { setIsProcessing(false); @@ -168,7 +179,7 @@ const ReportDetailPage: React.FC = () => { setIsUploadModalOpen(false)} + onClose={handleUploadComplete} onUploadComplete={handleUploadComplete} /> diff --git a/frontend/src/pages/Reports/ReportsListPage.tsx b/frontend/src/pages/Reports/ReportsListPage.tsx index 4d41ff7..2a6bc39 100644 --- a/frontend/src/pages/Reports/ReportsListPage.tsx +++ b/frontend/src/pages/Reports/ReportsListPage.tsx @@ -15,13 +15,12 @@ import { } from '@ionic/react'; import { useTranslation } from 'react-i18next'; import { useHistory, useLocation } from 'react-router-dom'; -import { useQuery, useQueryClient } from '@tanstack/react-query'; -import { fetchAllReports, toggleReportBookmark } from 'common/api/reportService'; -import { useMarkReportAsRead } from 'common/hooks/useReports'; +import { useQuery } from '@tanstack/react-query'; +import { fetchAllReports } from 'common/api/reportService'; +import { useMarkReportAsRead, useToggleReportBookmark } from 'common/hooks/useReports'; import ReportItem from 'pages/Home/components/ReportItem/ReportItem'; import NoReportsMessage from 'pages/Home/components/NoReportsMessage/NoReportsMessage'; import { useState, useMemo, useEffect, useRef } from 'react'; -import { MedicalReport } from 'common/models/medicalReport'; import sortSvg from 'assets/icons/sort.svg'; import filterOutlineIcon from 'assets/icons/filter-outline.svg'; import reportsIcon from 'assets/icons/reports.svg'; @@ -31,6 +30,7 @@ import CategoryTag from './components/CategoryTag/CategoryTag'; import ReportsFilterEmpty from './components/ReportsFilterEmpty/ReportsFilterEmpty'; import './ReportsListPage.scss'; +import { QueryKey } from 'common/utils/constants'; type FilterOption = 'all' | 'bookmarked'; type SortDirection = 'desc' | 'asc'; @@ -42,7 +42,7 @@ const ReportsListPage: React.FC = () => { const { t } = useTranslation(['report', 'common']); const history = useHistory(); const location = useLocation(); - const queryClient = useQueryClient(); + const toggleBookmark = useToggleReportBookmark(); const [filter, setFilter] = useState('all'); const [sortDirection, setSortDirection] = useState('desc'); // Default sort by newest first const [showToast, setShowToast] = useState(false); @@ -65,7 +65,7 @@ const ReportsListPage: React.FC = () => { isError, refetch, } = useQuery({ - queryKey: ['reports'], + queryKey: [QueryKey.Reports], queryFn: fetchAllReports, refetchOnMount: true, }); @@ -122,23 +122,6 @@ const ReportsListPage: React.FC = () => { history.push(`/tabs/reports/${reportId}`); }; - const handleToggleBookmark = async (reportId: string, isCurrentlyBookmarked: boolean) => { - try { - // Toggle the bookmark status - const updatedReport = await toggleReportBookmark(reportId, !isCurrentlyBookmarked); - - // Update the reports in the cache - queryClient.setQueryData(['reports'], (oldReports) => { - if (!oldReports) return []; - return oldReports.map((report) => - report.id === updatedReport.id ? updatedReport : report, - ); - }); - } catch (error) { - console.error('Failed to toggle bookmark:', error); - } - }; - const handleUpload = () => { history.push('/tabs/upload'); }; @@ -243,7 +226,9 @@ const ReportsListPage: React.FC = () => { key={report.id} report={report} onClick={() => handleReportClick(report.id)} - onToggleBookmark={() => handleToggleBookmark(report.id, report.bookmarked)} + onToggleBookmark={() => + toggleBookmark.mutate({ reportId: report.id, isBookmarked: report.bookmarked }) + } showBookmarkButton /> ));