11<script setup lang="ts">
2- import { computed , onMounted , ref } from ' vue' ;
2+ import { computed , onMounted , ref , watch } from ' vue' ;
3+ import { useRoute , useRouter } from ' vue-router' ;
34import {
45 VAlert ,
56 VBtn ,
@@ -13,9 +14,7 @@ import {
1314 VSpacer ,
1415} from ' vuetify/components' ;
1516
16- import SearchInputSection from ' @/components/organisms/search/SearchInputSection.vue' ;
1717import SearchResultsSection from ' @/components/organisms/search/SearchResultsSection.vue' ;
18-
1918import PdfViewerDialog from ' @/components/organisms/pdf/PdfViewerDialog.vue' ;
2019import type { Paper } from ' @/types/content' ;
2120
@@ -24,67 +23,75 @@ import { useAuthStore } from '@/stores/auth';
2423import { useProjectsStore } from ' @/stores/projects' ;
2524import { mapSearchResponseToPapers } from ' @/mappers/paper-mapper' ;
2625
26+ const route = useRoute ();
27+ const router = useRouter ();
2728const authStore = useAuthStore ();
2829const projectsStore = useProjectsStore ();
2930
30- // State to track the active search context
31- const hasActiveSearch = ref (false );
31+ // ----- state -----
3232const currentQueryText = ref (' ' );
3333const currentFile = ref <File | null >(null );
3434
3535const outputs = ref <Paper []>([]);
3636const isLoading = ref (false );
3737const errorMessage = ref <string | null >(null );
3838
39- // add-to-project dialog state
39+ // Dialog states
4040const addToProjectDialogOpen = ref (false );
4141const paperToAdd = ref <Paper | null >(null );
4242const selectedProjectIdForAdd = ref <number | null >(null );
43-
4443const pdfViewerOpen = ref (false );
4544const pdfPaperId = ref <number | null >(null );
4645const pdfPaperTitle = ref (' ' );
4746
48- // derived state
47+ // ----- derived state -----
4948const isAuthenticated = computed (() => authStore .isAuthenticated );
5049const projects = computed (() => projectsStore .projects );
5150const projectOptions = computed (() => projectsStore .projects );
5251
53- onMounted (() => {
52+ // ----- lifecycle -----
53+
54+ onMounted (async () => {
5455 if (isAuthenticated .value ) {
5556 projectsStore .loadProjects ();
5657 }
58+ // Trigger search immediately based on URL or State
59+ await initializeSearch ();
5760});
5861
59- // ----- search flow -----
60- type SearchPayload = { query: string ; file: File | null };
61-
62- function isSearchPayload(payload : unknown ): payload is SearchPayload {
63- return (
64- !! payload && typeof payload === ' object' &&
65- ' query' in payload &&
66- typeof (payload as any ).query === ' string' &&
67- ' file' in payload &&
68- (((payload as any ).file === null ) || (payload as any ).file instanceof File )
69- );
70- }
62+ // Watch for URL changes (e.g., user searches for something new inside the results page)
63+ watch (() => route .query .q , async (newQuery ) => {
64+ if (newQuery !== currentQueryText .value ) {
65+ await initializeSearch ();
66+ }
67+ });
7168
72- const handleSubmitQuery = async (payload : { query: string ; file: File | null } | string ) => {
73- const query = typeof payload === ' string' ? payload : payload .query ;
74- const file = isSearchPayload (payload ) ? payload .file : null ;
69+ // ----- search logic -----
70+
71+ const initializeSearch = async () => {
72+ // 1. Check history state for a File (passed from Home)
73+ const stateFile = history .state ?.file as File | undefined ;
74+ // 2. Check URL query for text
75+ const queryParam = route .query .q ?.toString () || ' ' ;
76+
77+ // If we have neither, usually we redirect to home, but let's just stay empty or load default
78+ if (! queryParam && ! stateFile ) {
79+ return ;
80+ }
81+
82+ await performSearch (queryParam , stateFile || null );
83+ };
7584
76- // Update state
77- currentQueryText .value = query || ' ' ;
78- currentFile .value = file || null ;
79- hasActiveSearch .value = true ;
85+ const performSearch = async (query : string , file : File | null ) => {
86+ currentQueryText .value = query ;
87+ currentFile .value = file ;
8088
8189 outputs .value = [];
8290 errorMessage .value = null ;
8391 isLoading .value = true ;
8492
8593 try {
8694 let response;
87-
8895 if (file ) {
8996 response = await searchPapersByPdf (file , query || undefined );
9097 } else {
@@ -99,13 +106,24 @@ const handleSubmitQuery = async (payload: { query: string; file: File | null } |
99106 }
100107};
101108
102- // ----- add-from-search flow -----
109+ // Handle new search from within the Results Page
110+ const handleUpdateQuery = (payload : { query: string ; file: File | null } | string ) => {
111+ const query = typeof payload === ' string' ? payload : payload .query ;
112+ const file = typeof payload !== ' string' && payload .file ? payload .file : null ;
103113
104- const handleAddFromSearch = (paper : Paper ) => {
105- if (! isAuthenticated .value || ! projects .value .length ) {
106- return ;
114+ // Update URL. This will trigger the `watch` above if only text changes.
115+ // If file changes, we might need to manually trigger because URL might not change.
116+ router .push ({ query: { q: query }, state: { file: file } });
117+
118+ // If there is a file, the URL watch might not catch it (if query string is same),
119+ // so we force execution if a file is present.
120+ if (file ) {
121+ performSearch (query , file );
107122 }
123+ };
108124
125+ const handleAddFromSearch = (paper : Paper ) => {
126+ if (! isAuthenticated .value || ! projects .value .length ) return ;
109127 paperToAdd .value = paper ;
110128 selectedProjectIdForAdd .value = projects .value [0 ]?.project_id ?? null ;
111129 addToProjectDialogOpen .value = true ;
@@ -116,12 +134,7 @@ const confirmAddToProject = async () => {
116134 addToProjectDialogOpen .value = false ;
117135 return ;
118136 }
119-
120- await projectsStore .addPaper (
121- selectedProjectIdForAdd .value ,
122- paperToAdd .value .paper_id
123- );
124-
137+ await projectsStore .addPaper (selectedProjectIdForAdd .value , paperToAdd .value .paper_id );
125138 addToProjectDialogOpen .value = false ;
126139 paperToAdd .value = null ;
127140};
@@ -143,21 +156,16 @@ const handleViewPaper = (paper: Paper) => {
143156 {{ errorMessage }}
144157 </v-alert >
145158
146- <div v-if =" !hasActiveSearch" >
147- <SearchInputSection @submit =" handleSubmitQuery " />
148- </div >
149- <div v-else >
150- <SearchResultsSection
151- :query =" currentQueryText "
152- :file =" currentFile "
153- :outputs =" outputs "
154- :show-abstract =" true "
155- :show-add =" isAuthenticated "
156- @add =" handleAddFromSearch "
157- @view =" handleViewPaper "
158- @update-query =" handleSubmitQuery "
159- />
160- </div >
159+ <SearchResultsSection
160+ :query =" currentQueryText "
161+ :file =" currentFile "
162+ :outputs =" outputs "
163+ :show-abstract =" true "
164+ :show-add =" isAuthenticated "
165+ @add =" handleAddFromSearch "
166+ @view =" handleViewPaper "
167+ @update-query =" handleUpdateQuery "
168+ />
161169
162170 <v-dialog v-model =" addToProjectDialogOpen" max-width =" 500" >
163171 <v-card >
0 commit comments