1+ import BotDirectory from '@app/components/BotDirectory/BotDirectory'
2+ import { useBotDirectory } from '@app/hook/useBotDirectory'
3+ import { MezonAppType } from '@app/enums/mezonAppType.enum'
4+ import SearchBar from '@app/mtb-ui/SearchBar/SearchBar'
5+ import MtbTypography from '@app/mtb-ui/Typography/Typography'
6+ import { useLazyMezonAppControllerSearchMezonAppQuery } from '@app/services/api/mezonApp/mezonApp'
7+ import { useLazyTagControllerGetTagsQuery } from '@app/services/api/tag/tag'
8+ import { RootState } from '@app/store'
9+ import { IMezonAppStore } from '@app/store/mezonApp'
10+ import { ApiError } from '@app/types/API.types'
11+ import { getPageFromParams } from '@app/utils/uri'
12+ import { Divider } from 'antd'
13+ import { useEffect , useMemo , useRef , useState } from 'react'
14+ import { useSelector } from 'react-redux'
15+ import { useTranslation } from 'react-i18next'
16+ import { useNavigate , useSearchParams } from 'react-router-dom'
17+ import { toast } from 'react-toastify'
18+ import { IBotSearchResultProps } from './BotSearchResult.types'
19+ import { ViewMode } from '@app/enums/viewMode.enum'
20+ import { BotDirectoryVariant } from '@app/enums/BotDirectory.enum'
21+
22+ function BotSearchResult ( { isSearchPage = false } : IBotSearchResultProps ) {
23+ const { t } = useTranslation ( [ 'home_page' ] )
24+ const navigate = useNavigate ( )
25+ const mainRef = useRef < HTMLDivElement > ( null )
26+ const { mezonApp } = useSelector < RootState , IMezonAppStore > ( ( s ) => s . mezonApp )
27+ const [ getTagList ] = useLazyTagControllerGetTagsQuery ( )
28+ const [ getMezonApp , { isError, error, isFetching } ] = useLazyMezonAppControllerSearchMezonAppQuery ( )
29+
30+ const [ searchParams , setSearchParams ] = useSearchParams ( )
31+ const queryParam = searchParams . get ( 'q' ) ;
32+ const pageFromUrl = getPageFromParams ( searchParams ) ;
33+ const {
34+ page,
35+ setPage,
36+ pageSize,
37+ viewMode,
38+ sortField,
39+ sortOrder,
40+ selectedSort,
41+ sortOptions,
42+ handleViewModeChange,
43+ handlePageSizeChange,
44+ handleSortChange
45+ } = useBotDirectory ( { initialViewMode : ViewMode . LIST , initialPage : pageFromUrl } ) ;
46+
47+ const defaultSearchQuery = useMemo ( ( ) => queryParam ?. trim ( ) || '' , [ queryParam ] ) ;
48+ const defaultTagIds = useMemo (
49+ ( ) => searchParams . get ( 'tags' ) ?. split ( ',' ) . filter ( Boolean ) || [ ] ,
50+ [ searchParams . get ( 'tags' ) ]
51+ )
52+ const defaultType = searchParams ?. get ( 'type' ) as MezonAppType | undefined
53+
54+ const [ isInitialized , setIsInitialized ] = useState < boolean > ( false )
55+ const [ searchQuery , setSearchQuery ] = useState < string > ( searchParams . get ( 'q' ) ?. trim ( ) || '' )
56+ const [ tagIds , setTagIds ] = useState < string [ ] > ( searchParams . get ( 'tags' ) ?. split ( ',' ) . filter ( Boolean ) || [ ] )
57+ const [ type , setType ] = useState < MezonAppType | undefined > ( defaultType )
58+
59+ useEffect ( ( ) => {
60+ getTagList ( )
61+ } , [ ] )
62+
63+ useEffect ( ( ) => {
64+ const newPage = getPageFromParams ( searchParams )
65+ if ( newPage !== page ) {
66+ setPage ( newPage )
67+ }
68+ } , [ searchParams ] )
69+
70+ useEffect ( ( ) => {
71+ if ( ! isSearchPage ) return ;
72+ setSearchQuery ( defaultSearchQuery ) ;
73+ setTagIds ( defaultTagIds ) ;
74+ setType ( defaultType ) ;
75+
76+ if ( isInitialized || defaultSearchQuery || defaultTagIds . length || defaultType ) {
77+ searchMezonAppList ( defaultSearchQuery , defaultTagIds , defaultType ) ;
78+ }
79+ setIsInitialized ( true ) ;
80+ } , [ searchParams , page , pageSize , selectedSort ] ) ;
81+
82+ useEffect ( ( ) => {
83+ if ( isError && error ) {
84+ const apiError = error as ApiError
85+ if ( apiError ?. status === 404 || apiError ?. data ?. statusCode === 404 ) {
86+ navigate ( '/404' )
87+ } else {
88+ toast . error ( apiError ?. data ?. message )
89+ }
90+ }
91+ } , [ isError , error ] )
92+
93+ useEffect ( ( ) => {
94+ searchMezonAppList ( searchQuery , tagIds , type )
95+ } , [ page , pageSize , isSearchPage , selectedSort ] )
96+
97+ const searchMezonAppList = ( searchQuery ?: string , tagIds ?: string [ ] , type ?: MezonAppType ) => {
98+ getMezonApp ( {
99+ search : isSearchPage ? searchQuery : undefined ,
100+ tags : tagIds && tagIds . length ? tagIds : undefined ,
101+ type,
102+ pageNumber : page ,
103+ pageSize : pageSize ,
104+ sortField : sortField ,
105+ sortOrder : sortOrder
106+ } )
107+ }
108+
109+ const handleMainPageChange = ( newPage : number ) => {
110+ setPage ( newPage ) ;
111+ const newParams = new URLSearchParams ( searchParams )
112+ newParams . set ( 'page' , newPage . toString ( ) )
113+ setSearchParams ( newParams )
114+ if ( mainRef . current ) {
115+ mainRef . current . scrollIntoView ( { behavior : 'auto' } )
116+ }
117+ }
118+
119+ const onPressSearch = ( text : string , tagIds ?: string [ ] , type ?: MezonAppType ) => {
120+ setSearchQuery ( text )
121+ setTagIds ( tagIds ?? [ ] )
122+ setType ( type )
123+ if ( page !== 1 ) {
124+ setPage ( 1 )
125+ return
126+ }
127+ searchMezonAppList ( text , tagIds , type )
128+ }
129+
130+ return (
131+ < div ref = { mainRef } className = 'flex flex-col justify-center pt-8 pb-12 max-w-6xl mx-auto relative z-1 md:px-6 px-2 w-full' >
132+ < Divider variant = 'solid' className = '!border-bg-secondary' >
133+ < MtbTypography variant = 'h1' customClassName = 'max-md:whitespace-normal' >
134+ { t ( 'homepage.explore_title' ) }
135+ </ MtbTypography >
136+ </ Divider >
137+ < div className = 'pt-3' >
138+ < SearchBar
139+ onSearch = { ( val , tagIds , type ) => onPressSearch ( val ?? '' , tagIds , type ) }
140+ defaultValue = { searchQuery }
141+ isResultPage = { isSearchPage }
142+ > </ SearchBar >
143+ </ div >
144+
145+ < div className = 'pt-8' >
146+ < BotDirectory
147+ variant = { BotDirectoryVariant . FULL }
148+ data = { mezonApp ?. data || [ ] }
149+ isLoading = { isFetching }
150+ currentPage = { page }
151+ pageSize = { pageSize }
152+ totalCount = { mezonApp ?. totalCount || 0 }
153+ viewMode = { viewMode }
154+ onPageChange = { handleMainPageChange }
155+ onPageSizeChange = { handlePageSizeChange }
156+ onViewModeChange = { handleViewModeChange }
157+ sortOption = { selectedSort }
158+ sortOptions = { sortOptions }
159+ onSortChange = { handleSortChange }
160+ isPublic = { true }
161+ />
162+ </ div >
163+ </ div >
164+ )
165+ }
166+
167+ export default BotSearchResult
0 commit comments