11"use client" ;
22
3- import { useState , useEffect , useCallback } from "react" ;
3+ import { useState , useEffect , useCallback , Suspense } from "react" ;
44import { useRouter , useSearchParams } from "next/navigation" ;
55import { libraryApi , Library } from "@/lib/api" ;
66import {
@@ -28,7 +28,7 @@ import {
2828import LibraryCardSkeleton from "@/components/skeletons/LibraryCardSkeleton" ;
2929import toast from "react-hot-toast" ;
3030
31- export default function ExplorePage ( ) {
31+ function ExploreContent ( ) {
3232 const router = useRouter ( ) ;
3333 const searchParams = useSearchParams ( ) ;
3434 const [ libraries , setLibraries ] = useState < Library [ ] > ( [ ] ) ;
@@ -75,7 +75,6 @@ export default function ExplorePage() {
7575 } else {
7676 setError ( "Не удалось загрузить данные" ) ;
7777 }
78- // Toast показываем только если это не отмена запроса (если бы была)
7978 toast . error ( "Не удалось загрузить библиотеки" ) ;
8079 } finally {
8180 setLoading ( false ) ;
@@ -88,8 +87,6 @@ export default function ExplorePage() {
8887 const page = parseInt ( searchParams . get ( "page" ) || "0" ) ;
8988 const searchQuery = searchParams . get ( "q" ) || "" ;
9089
91- // Всегда синхронизируем input с URL (source of truth)
92- // Это безопасно, так как эффект запускается только при изменении URL (searchParams)
9390 setQuery ( searchQuery ) ;
9491 setCurrentPage ( page ) ;
9592 fetchLibraries ( page , searchQuery ) ;
@@ -107,13 +104,10 @@ export default function ExplorePage() {
107104 _event : React . ChangeEvent < unknown > ,
108105 page : number ,
109106 ) => {
110- const newPage = page - 1 ; // Material-UI использует 1-based индексацию
111-
112- // Обновляем URL
107+ const newPage = page - 1 ;
113108 const params = new URLSearchParams ( searchParams . toString ( ) ) ;
114109 params . set ( "page" , newPage . toString ( ) ) ;
115110 router . push ( `/explore?${ params . toString ( ) } ` ) ;
116-
117111 window . scrollTo ( { top : 0 , behavior : "smooth" } ) ;
118112 } ;
119113
@@ -131,7 +125,6 @@ export default function ExplorePage() {
131125
132126 return (
133127 < Box sx = { { minHeight : "100vh" , pb : 8 } } >
134- { /* Hero Section */ }
135128 < Box
136129 sx = { {
137130 backgroundImage : `
@@ -179,15 +172,10 @@ export default function ExplorePage() {
179172 Находите и анализируйте Open Source библиотеки из различных
180173 экосистем
181174 </ Typography >
182-
183- { /* Search Box */ }
184175 < Box
185176 component = "form"
186177 onSubmit = { handleSearch }
187- sx = { {
188- maxWidth : 800 ,
189- mx : "auto" ,
190- } }
178+ sx = { { maxWidth : 800 , mx : "auto" } }
191179 >
192180 < Box
193181 sx = { {
@@ -211,7 +199,8 @@ export default function ExplorePage() {
211199 disableUnderline : true ,
212200 startAdornment : (
213201 < InputAdornment position = "start" >
214- < Search sx = { { color : "primary.main" } } />
202+ { " " }
203+ < Search sx = { { color : "primary.main" } } /> { " " }
215204 </ InputAdornment >
216205 ) ,
217206 sx : { px : 2 } ,
@@ -221,17 +210,11 @@ export default function ExplorePage() {
221210 type = "submit"
222211 variant = "contained"
223212 size = "large"
224- sx = { {
225- px : 4 ,
226- minWidth : 120 ,
227- boxShadow : "none" ,
228- } }
213+ sx = { { px : 4 , minWidth : 120 , boxShadow : "none" } }
229214 >
230215 Найти
231216 </ Button >
232217 </ Box >
233-
234- { /* Quick Filters */ }
235218 < Stack
236219 direction = "row"
237220 spacing = { 1 }
@@ -267,11 +250,9 @@ export default function ExplorePage() {
267250 </ Container >
268251 </ Box >
269252
270- { /* Results Section */ }
271253 < Container maxWidth = "lg" sx = { { mt : 6 } } >
272254 { loading ? (
273255 < >
274- { /* Results Header Skeleton */ }
275256 < Box
276257 sx = { {
277258 display : "flex" ,
@@ -281,11 +262,10 @@ export default function ExplorePage() {
281262 } }
282263 >
283264 < Typography variant = "h6" fontWeight = { 600 } >
284- Загрузка...
265+ { " " }
266+ Загрузка...{ " " }
285267 </ Typography >
286268 </ Box >
287-
288- { /* Library Cards Skeleton */ }
289269 < Box
290270 sx = { {
291271 display : "grid" ,
@@ -305,18 +285,15 @@ export default function ExplorePage() {
305285 ) : error ? (
306286 < Alert
307287 severity = "error"
308- sx = { {
309- borderRadius : 2 ,
310- "& .MuiAlert-message" : {
311- width : "100%" ,
312- } ,
313- } }
288+ sx = { { borderRadius : 2 , "& .MuiAlert-message" : { width : "100%" } } }
314289 >
315290 < Typography variant = "body1" fontWeight = { 600 } gutterBottom >
316- { error }
291+ { " " }
292+ { error } { " " }
317293 </ Typography >
318294 < Typography variant = "body2" sx = { { mt : 1 } } >
319- Для запуска бэкенда выполните:
295+ { " " }
296+ Для запуска бэкенда выполните:{ " " }
320297 </ Typography >
321298 < Box
322299 component = "code"
@@ -335,7 +312,6 @@ export default function ExplorePage() {
335312 </ Alert >
336313 ) : (
337314 < >
338- { /* Results Header */ }
339315 < Box
340316 sx = { {
341317 display : "flex" ,
@@ -357,8 +333,6 @@ export default function ExplorePage() {
357333 Фильтры
358334 </ Button >
359335 </ Box >
360-
361- { /* Library Cards */ }
362336 < Box
363337 sx = { {
364338 display : "grid" ,
@@ -392,7 +366,6 @@ export default function ExplorePage() {
392366 onClick = { ( ) => router . push ( `/dashboard?libraryId=${ lib . id } ` ) }
393367 >
394368 < CardContent sx = { { flexGrow : 1 , p : 3 } } >
395- { /* Header */ }
396369 < Box
397370 sx = { {
398371 display : "flex" ,
@@ -439,33 +412,27 @@ export default function ExplorePage() {
439412 fontWeight = { 600 }
440413 sx = { { color : getHealthScoreColor ( lib . healthScore ) } }
441414 >
442- { lib . healthScore } %
415+ { " " }
416+ { lib . healthScore } %{ " " }
443417 </ Typography >
444418 </ Box >
445419 </ Box >
446-
447- { /* Title */ }
448420 < Typography
449421 variant = "h6"
450422 gutterBottom
451423 fontWeight = { 700 }
452424 sx = { { mb : 1 } }
453425 >
454- { lib . name }
426+ { " " }
427+ { lib . name } { " " }
455428 </ Typography >
456-
457429 < Typography
458430 variant = "caption"
459- sx = { {
460- display : "block" ,
461- color : "text.secondary" ,
462- mb : 2 ,
463- } }
431+ sx = { { display : "block" , color : "text.secondary" , mb : 2 } }
464432 >
465- v{ lib . version }
433+ { " " }
434+ v{ lib . version } { " " }
466435 </ Typography >
467-
468- { /* Description */ }
469436 < Typography
470437 variant = "body2"
471438 color = "text.secondary"
@@ -481,8 +448,6 @@ export default function ExplorePage() {
481448 >
482449 { lib . description || "Описание отсутствует" }
483450 </ Typography >
484-
485- { /* Metrics */ }
486451 < Box sx = { { mb : 2 } } >
487452 < Stack direction = "row" spacing = { 2 } sx = { { mb : 1 } } >
488453 < Tooltip title = "Рейтинг здоровья" >
@@ -524,8 +489,6 @@ export default function ExplorePage() {
524489 ) }
525490 </ Stack >
526491 </ Box >
527-
528- { /* Footer */ }
529492 < Box
530493 sx = { {
531494 mt : "auto" ,
@@ -550,29 +513,23 @@ export default function ExplorePage() {
550513 </ CardContent >
551514 </ Card >
552515 ) ) }
553-
554516 { libraries . length === 0 && (
555- < Box
556- sx = { {
557- gridColumn : "1 / -1" ,
558- textAlign : "center" ,
559- py : 10 ,
560- } }
561- >
517+ < Box sx = { { gridColumn : "1 / -1" , textAlign : "center" , py : 10 } } >
562518 < Search
563519 sx = { { fontSize : 64 , color : "text.disabled" , mb : 2 } }
564520 />
565521 < Typography variant = "h6" color = "text.secondary" gutterBottom >
566- Библиотеки не найдены
522+ { " " }
523+ Библиотеки не найдены{ " " }
567524 </ Typography >
568525 < Typography variant = "body2" color = "text.secondary" >
569- Попробуйте изменить запрос или выберите другую экосистему
526+ { " " }
527+ Попробуйте изменить запрос или выберите другую
528+ экосистему{ " " }
570529 </ Typography >
571530 </ Box >
572531 ) }
573532 </ Box >
574-
575- { /* Pagination */ }
576533 { totalPages > 1 && (
577534 < Box sx = { { display : "flex" , justifyContent : "center" , mt : 6 } } >
578535 < Pagination
@@ -583,11 +540,7 @@ export default function ExplorePage() {
583540 size = "large"
584541 showFirstButton
585542 showLastButton
586- sx = { {
587- "& .MuiPaginationItem-root" : {
588- fontWeight : 600 ,
589- } ,
590- } }
543+ sx = { { "& .MuiPaginationItem-root" : { fontWeight : 600 } } }
591544 />
592545 </ Box >
593546 ) }
@@ -597,3 +550,24 @@ export default function ExplorePage() {
597550 </ Box >
598551 ) ;
599552}
553+
554+ export default function ExplorePage ( ) {
555+ return (
556+ < Suspense
557+ fallback = {
558+ < Box
559+ sx = { {
560+ minHeight : "100vh" ,
561+ display : "flex" ,
562+ alignItems : "center" ,
563+ justifyContent : "center" ,
564+ } }
565+ >
566+ < Typography > Загрузка...</ Typography >
567+ </ Box >
568+ }
569+ >
570+ < ExploreContent />
571+ </ Suspense >
572+ ) ;
573+ }
0 commit comments