1- import { useEffect , useRef , useState } from 'react' ;
2-
1+ import { useCallback , useEffect , useState } from 'react' ;
32import { useDispatch , useSelector } from 'react-redux' ;
43import { Link } from 'react-router-dom' ;
54
@@ -25,153 +24,184 @@ import {
2524import '@/style/blogs.scss' ;
2625import { BLOGPAGE } from '@/share/CONST_DATA' ;
2726
27+ const IMAGES_PER_PAGE = 12 ;
28+
2829const BlogResultsList : React . FC = ( ) => {
2930 const dispatch : AppDispatch = useDispatch ( ) ;
3031 const { blogURL } = usePageRouter ( ) ;
32+
33+ // Selectors
3134 const {
3235 data : BlogData ,
3336 searchAutoKey,
3437 searchAutoValue,
35- } = useSelector (
36- ( state : RootState ) => state . getBlogData as InitialStateBlogProps ,
37- ) ;
38+ } = useSelector ( ( state : RootState ) => state . getBlogData as InitialStateBlogProps ) ;
39+
40+ const { languageValue } = useSelector ( ( state : RootState ) => state . getLanguages ) ;
41+ const { inputSearchValue } = useSelector ( ( state : RootState ) => state . getCommonGlobalSearch ) ;
42+
43+ // Local state
3844 const [ totalResultsCount , setTotalResultsCount ] = useState ( 0 ) ;
3945 const [ page , setPage ] = useState < number > ( 1 ) ;
40-
41- const imagesPerPage = 12 ;
42- const { languageValue } = useSelector (
43- ( state : RootState ) => state . getLanguages ,
44- ) ;
4546 const [ loading , setLoading ] = useState ( false ) ;
46- const { inputSearchValue } = useSelector (
47- ( state : RootState ) => state . getCommonGlobalSearch ,
48- ) ;
4947
50- const effectOnce = useRef ( false ) ;
51- const fetchDataBlog = async ( ) => {
52- setLoading ( true ) ;
48+ // Build filters based on current state
49+ const buildFilters = useCallback ( ( ) : BlogFilter [ ] => {
5350 const filters : BlogFilter [ ] = [ ] ;
51+
52+ // Add language filter if present
5453 if ( languageValue ) {
5554 filters . push ( {
5655 varName : 'language' ,
5756 searchTerm : [ languageValue ] ,
5857 op : 'in' ,
5958 } ) ;
6059 }
61- if ( searchAutoValue || blogURL ) {
62- const convertURL = reverseFormatTextURL ( blogURL ! ) ;
60+
61+ // Add search filter based on priority: searchAutoValue > blogURL
62+ // If searchAutoValue exists (even if empty string), use it and ignore blogURL
63+ // If searchAutoValue is null/undefined, fall back to blogURL
64+ let searchTerm = null ;
65+
66+ if ( searchAutoValue !== null && searchAutoValue !== undefined ) {
67+ // searchAutoValue is explicitly set (could be empty string or non-empty)
68+ searchTerm = searchAutoValue . trim ( ) ; // Use trimmed value, empty string if originally empty
69+ } else if ( blogURL ) {
70+ // No searchAutoValue set, use blogURL as fallback
71+ searchTerm = reverseFormatTextURL ( blogURL ) ;
72+ }
73+
74+ // Only add search filter if we have a non-empty search term and searchAutoKey
75+ if ( searchTerm && searchAutoKey ) {
6376 filters . push ( {
6477 varName : searchAutoKey ,
65- searchTerm : [ searchAutoValue || convertURL ! ] ,
66- op : 'in ' ,
78+ searchTerm : searchTerm ,
79+ op : 'icontains ' ,
6780 } ) ;
6881 }
82+
83+ return filters ;
84+ } , [ languageValue , searchAutoValue , searchAutoKey , blogURL ] ) ;
6985
70- const dataSend : BlogDataPropsRequest = {
71- filter : filters || [ ] ,
72- page : page ,
73- page_size : imagesPerPage ,
74- } ;
75-
76- if ( inputSearchValue ) {
77- dataSend [ 'global_search' ] = inputSearchValue ;
78- }
79-
86+ // Fetch blog data
87+ const fetchDataBlog = useCallback ( async ( ) => {
88+ setLoading ( true ) ;
89+
8090 try {
91+ const filters = buildFilters ( ) ;
92+ const dataSend : BlogDataPropsRequest = {
93+ filter : filters ,
94+ page : page ,
95+ page_size : IMAGES_PER_PAGE ,
96+ } ;
97+
98+ // Add global search if present
99+ if ( inputSearchValue ?. trim ( ) ) {
100+ dataSend . global_search = inputSearchValue . trim ( ) ;
101+ }
102+
81103 const response = await dispatch ( fetchBlogData ( dataSend ) ) . unwrap ( ) ;
82104
83105 if ( response ) {
84106 const { results, count } = response ;
85-
86107 dispatch ( setBlogData ( results ) ) ;
87- setTotalResultsCount ( ( ) => Number ( count ) ) ;
88- setLoading ( false ) ;
108+ setTotalResultsCount ( Number ( count ) || 0 ) ;
89109 }
90110 } catch ( error ) {
111+ console . error ( 'Failed to fetch blog data:' , error ) ;
112+ // Optionally dispatch an error action or show error state
113+ dispatch ( setBlogData ( [ ] ) ) ;
114+ setTotalResultsCount ( 0 ) ;
115+ } finally {
91116 setLoading ( false ) ;
92- console . log ( 'error' , error ) ;
93117 }
94- } ;
118+ } , [ dispatch , buildFilters , page , inputSearchValue ] ) ;
95119
120+ // Reset to first page when filters change
121+ const resetToFirstPage = useCallback ( ( ) => {
122+ setPage ( 1 ) ;
123+ } , [ ] ) ;
124+
125+ // Effect to fetch data when dependencies change
126+ useEffect ( ( ) => {
127+ fetchDataBlog ( ) ;
128+ } , [ fetchDataBlog ] ) ;
129+
130+ // Effect to reset page when filters change (but not page itself)
131+ useEffect ( ( ) => {
132+ resetToFirstPage ( ) ;
133+ } , [ languageValue , inputSearchValue , searchAutoKey , searchAutoValue , blogURL , resetToFirstPage ] ) ;
134+
135+ // Cleanup on unmount
96136 useEffect ( ( ) => {
97- if ( ! effectOnce . current ) {
98- fetchDataBlog ( ) ;
99- }
100137 return ( ) => {
101138 dispatch ( setBlogPost ( { } as BlogDataProps ) ) ;
102139 } ;
103- } , [
104- dispatch ,
105- languageValue ,
106- inputSearchValue ,
107- page ,
108- searchAutoKey ,
109- searchAutoValue ,
110- ] ) ;
140+ } , [ dispatch ] ) ;
111141
142+ // Render blog card
143+ const renderBlogCard = ( blog : BlogDataProps ) => (
144+ < div className = "card" key = { `${ blog . id } -${ blog . title } ` } >
145+ < Link to = { `/${ BLOGPAGE } /${ formatTextURL ( blog . title ) } /${ blog . id } ` } >
146+ < img
147+ src = { blog . thumbnail ? `${ BASEURL } ${ blog . thumbnail } ` : defaultImage }
148+ alt = { blog . title }
149+ className = { blog . thumbnail ? "card-img img-fluid content-image" : "" }
150+ style = { ! blog . thumbnail ? { textAlign : 'center' , width : '100%' } : undefined }
151+ />
152+ < div className = "content-details fadeIn-bottom" >
153+ < h3 className = "content-title" > { blog . title } </ h3 >
154+ { blog . subtitle && < h4 className = "content-title" > { blog . subtitle } </ h4 > }
155+ { blog . authors ?. map ( ( author , index ) => (
156+ < p key = { `${ author . id } -${ author . name } ` } >
157+ { index > 0 && ' | ' }
158+ { author . name }
159+ </ p >
160+ ) ) }
161+ </ div >
162+ </ Link >
163+ </ div >
164+ ) ;
165+
166+ // Loading state
112167 if ( loading ) {
113168 return (
114169 < div className = "loading-logo" >
115- < img src = { LOADINGLOGO } alt = "loading " />
170+ < img src = { LOADINGLOGO } alt = "Loading... " />
116171 </ div >
117172 ) ;
118173 }
119174
175+ const displayTitle = blogURL ? reverseFormatTextURL ( blogURL ) : '' ;
176+ const hasData = BlogData && BlogData . length > 0 ;
177+
120178 return (
121179 < >
122- < div id = "blog_intro" className = "blog_intro" >
123- < h2 > { reverseFormatTextURL ( blogURL ! ) } </ h2 >
124- </ div >
125- { BlogData . length > 0 ? (
180+ { displayTitle && (
181+ < div id = "blog_intro" className = "blog_intro" >
182+ < h2 > { displayTitle } </ h2 >
183+ </ div >
184+ ) }
185+
186+ { hasData ? (
126187 < div className = { blogURL ? 'container-new-with-intro' : 'container-new' } >
127188 < div className = "card-columns" >
128- { BlogData . length > 0 &&
129- BlogData . map ( ( value ) => (
130- < div className = "card" key = { `${ value . id } ${ value . title } ` } >
131- < Link
132- to = { `/${ BLOGPAGE } /${ formatTextURL ( value . title ) } /${
133- value . id
134- } `}
135- >
136- { value . thumbnail ? (
137- < img
138- src = { `${ BASEURL } ${ value . thumbnail } ` }
139- alt = { value . title }
140- className = "card-img img-fluid content-image "
141- />
142- ) : (
143- < img
144- src = { defaultImage }
145- alt = { value . title }
146- style = { { textAlign : 'center' , width : '100%' } }
147- />
148- ) }
149- < div className = "content-details fadeIn-bottom" >
150- < h3 className = "content-title" > { value . title } </ h3 >
151- < h4 className = "content-title" > { value . subtitle } </ h4 >
152- { value . authors . map ( ( name , index ) => (
153- < p key = { `${ name . id } ${ name . name } ` } >
154- { index > 0 && ' | ' }
155- { name . name }
156- </ p >
157- ) ) }
158- </ div >
159- </ Link >
160- </ div >
161- ) ) }
189+ { BlogData . map ( renderBlogCard ) }
162190 </ div >
191+
163192 < BlogPageButton
164193 setCurrentBlogPage = { setPage }
165194 currentBlogPage = { page }
166195 BlogData = { BlogData }
167- imagesPerPage = { imagesPerPage }
196+ imagesPerPage = { IMAGES_PER_PAGE }
168197 count = { totalResultsCount }
169198 />
170199 </ div >
171200 ) : (
172- < NoDataState text = { reverseFormatTextURL ( blogURL ! ) } />
201+ < NoDataState text = { displayTitle || 'No blogs found' } />
173202 ) }
174203 </ >
175204 ) ;
176205} ;
177- export default BlogResultsList ;
206+
207+ export default BlogResultsList ;
0 commit comments