11'use client' 
2+ /************************************************************************************************ 
3+  ** SupportArticleList Component: 
4+  ** 
5+  ** Client component for displaying paginated lists of support articles 
6+  ** Features interactive pagination and loading states 
7+  ** 
8+  ** Features: 
9+  ** - Pagination with next/previous controls 
10+  ** - Loading skeleton for better UX 
11+  ** - Empty state handling 
12+  ** - Responsive grid layout for different viewports 
13+  ** 
14+  ** Usage: 
15+  ** - Import in support list pages 
16+  ** - Configure with useFetchSupportArticles hook 
17+  ** - Add custom empty state message if needed 
18+  ************************************************************************************************/ 
219import  { useParams ,  useRouter ,  useSearchParams }  from  'next/navigation' 
320import  { Fragment ,  useEffect ,  useMemo ,  useState }  from  'react' 
421import  ReactPaginate  from  'react-paginate' 
@@ -53,8 +70,9 @@ export function SupportArticleList({
5370	// determine the active tag (from URL or prop) 
5471	const  activeTag  =  urlTag  ??  tag 
5572
56- 	// server-side filtering: always use the configured pageSize; grouped view will show previews and 
57- 	// provide "View all" links to the paginated tag view instead of fetching the entire dataset. 
73+ 	// server-side filtering: always fetch the configured pageSize from the server so 
74+ 	// tag discovery and counts are accurate. For grouped preview mode we still limit 
75+ 	// the visual preview to 9 items per tag in the UI (see .slice below). 
5876	const  fetchPage  =  activeTag  ? page  : 1 
5977	const  fetchPageSize  =  pageSize 
6078
@@ -83,6 +101,46 @@ export function SupportArticleList({
83101		return  Array . from ( new  Set ( filteredArticles . flatMap ( a  =>  a . tags  ??  [ ] ) ) ) . sort ( ) 
84102	} ,  [ filteredArticles ] ) 
85103
104+ 	// Preserve a master list of tags so that when a single tag is active we 
105+ 	// still can display all available tags (like radio buttons) instead of 
106+ 	// hiding the other options. We populate `allTags` from the groupedTags 
107+ 	// when available, and as a fallback we prefetch an unfiltered page to 
108+ 	// discover tags when the page is loaded already filtered by tag. 
109+ 	const  [ allTags ,  setAllTags ]  =  useState < string [ ]  |  null > ( null ) 
110+ 
111+ 	// Prefetch unfiltered articles only when we don't yet have `allTags`. 
112+ 	const  { articles : discoveryArticles }  =  useFetchSupportArticles ( { 
113+ 		page : 1 , 
114+ 		pageSize, 
115+ 		sort, 
116+ 		populateContent : false , 
117+ 		cacheArticles : false , 
118+ 		tag : undefined , 
119+ 		search : undefined , 
120+ 		skip : allTags  !==  null 
121+ 	} ) 
122+ 
123+ 	useEffect ( ( )  =>  { 
124+ 		if  ( ! activeTag  &&  groupedTags . length  >  0  &&  allTags  ===  null )  { 
125+ 			setAllTags ( groupedTags ) 
126+ 		} 
127+ 	} ,  [ activeTag ,  groupedTags ,  allTags ] ) 
128+ 
129+ 	useEffect ( ( )  =>  { 
130+ 		if  ( discoveryArticles  &&  discoveryArticles . length  >  0 )  { 
131+ 			const  discovered  =  Array . from ( new  Set ( discoveryArticles . flatMap ( a  =>  a . tags  ??  [ ] ) ) ) . sort ( ) 
132+ 			// Merge existing allTags (if any), discovered tags, and the activeTag so 
133+ 			// we don't accidentally drop the currently active tag if it's not 
134+ 			// present on the discovery page. 
135+ 			const  merged  =  Array . from ( 
136+ 				new  Set ( [ ...( allTags  ??  [ ] ) ,  ...( discovered  ??  [ ] ) ,  ...( activeTag  ? [ activeTag ]  : [ ] ) ] ) 
137+ 			) . sort ( ) 
138+ 			if  ( allTags  ===  null  ||  merged . join ( '|' )  !==  ( allTags  ||  [ ] ) . join ( '|' ) )  { 
139+ 				setAllTags ( merged ) 
140+ 			} 
141+ 		} 
142+ 	} ,  [ allTags ,  discoveryArticles ,  activeTag ] ) 
143+ 
86144	const  groupedArticles  =  useMemo ( ( )  =>  { 
87145		const  map : Record < string ,  TSupportArticle [ ] >  =  { } 
88146		for  ( const  t  of  groupedTags )  { 
@@ -117,6 +175,8 @@ export function SupportArticleList({
117175		return  < SupportArticleListSkeleton  pageSize = { pageSize }  /> 
118176	} 
119177
178+ 	const  displayTags  =  allTags  ??  groupedTags 
179+ 
120180	return  ( 
121181		< Fragment > 
122182			< div  className = { 'container mx-auto' } > 
@@ -130,16 +190,7 @@ export function SupportArticleList({
130190				< div  className = { 'flex w-full justify-center' } > 
131191					< div  className = { 'w-1/2' } > 
132192						< SupportTags 
133- 							tags = { 
134- 								activeTag 
135- 									? ( ( )  =>  { 
136- 											const  derived  =  Array . from ( 
137- 												new  Set ( ( articles  ||  [ ] ) . flatMap ( a  =>  a . tags  ??  [ ] ) ) 
138- 											) 
139- 											return  derived . length  >  0  ? derived  : [ activeTag ] 
140- 										} ) ( ) 
141- 									: groupedTags 
142- 							} 
193+ 							tags = { displayTags } 
143194							active = { activeTag } 
144195							onClick = { ( t : string  |  null )  =>  { 
145196								const  params  =  new  URLSearchParams ( Array . from ( searchParams  ||  [ ] ) ) 
@@ -168,37 +219,42 @@ export function SupportArticleList({
168219						role = { 'status' } > 
169220						{ emptyMessage } 
170221					</ p > 
171- 				)  : // If a tag is active, show the existing paginated grid. Otherwise show grouped-by-tag sections. 
172- 				activeTag  ? ( 
173- 					< div  className = { 'mb-20' } > 
174- 						< div  className = { 'mb-4' } > 
175- 							< h2  className = { 'text-2xl' } > { activeTag } </ h2 > 
176- 						</ div > 
177- 						< div  className = { cl ( 'grid gap-6 md:grid-cols-2 lg:grid-cols-3' ,  gridClassName ) } > 
178- 							{ filteredArticles . map ( ( article : TSupportArticle )  =>  ( 
179- 								< ArticleCard 
180- 									key = { article . slug } 
181- 									article = { article } 
182- 									tagName = { activeTag } 
183- 									lang = { lang } 
184- 								/> 
185- 							) ) } 
186- 						</ div > 
187- 					</ div > 
188222				)  : ( 
189223					< div  className = { 'space-y-12 mb-20' } > 
190224						{ groupedTags . map ( tagName  =>  ( 
191225							< div  key = { tagName } > 
192- 								< div  className = { 'mb-4 flex items-center justify-between' } > 
193- 									< h2  className = { 'text-2xl' } > { tagName } </ h2 > 
194- 									< LocalizedLink 
195- 										className = { 'text-sm text-blue-400' } 
196- 										href = { `/${ lang } ${ encodeURIComponent ( tagName ) }  } > 
197- 										{ 'View all' } 
198- 									</ LocalizedLink > 
226+ 								< div  className = { 'mb-4' } > 
227+ 									< div  className = { 'flex items-center justify-between' } > 
228+ 										< div  className = { 'flex items-center gap-4' } > 
229+ 											< h2  className = { 'text-2xl' } > { tagName } </ h2 > 
230+ 											{ /* Desktop inline View all — hide when a tag/search is active */ } 
231+ 											{ ! activeTag  &&  ! searchQuery  &&  ( 
232+ 												< LocalizedLink 
233+ 													className = { 'hidden lg:inline-block text-sm text-gray-400' } 
234+ 													href = { `/${ lang } ${ encodeURIComponent ( tagName ) }  } > 
235+ 													{ 'View all' } 
236+ 												</ LocalizedLink > 
237+ 											) } 
238+ 										</ div > 
239+ 										{ /* Mobile: small 'View all' under title (visible on sm and down) */ } 
240+ 										{ ! activeTag  &&  ! searchQuery  &&  ( 
241+ 											< div  className = { 'block lg:hidden' } > 
242+ 												< LocalizedLink 
243+ 													className = { 'text-sm text-gray-400' } 
244+ 													href = { `/${ lang } ${ encodeURIComponent ( tagName ) }  } > 
245+ 													{ 'View all' } 
246+ 												</ LocalizedLink > 
247+ 											</ div > 
248+ 										) } 
249+ 									</ div > 
199250								</ div > 
200- 								< div  className = { cl ( 'grid gap-6 md:grid-cols-2 lg:grid-cols-3' ,  gridClassName ) } > 
201- 									{ ( groupedArticles [ tagName ]  ??  [ ] ) . slice ( 0 ,  3 ) . map ( ( article : TSupportArticle )  =>  ( 
251+ 								{ /* Render up to 3 rows (9 cards) in preview grouped mode, in case there are many items */ } 
252+ 								< div 
253+ 									className = { cl ( 
254+ 										'grid gap-6 grid-cols-1 md:grid-cols-2 lg:grid-cols-3' , 
255+ 										gridClassName 
256+ 									) } > 
257+ 									{ ( groupedArticles [ tagName ]  ??  [ ] ) . slice ( 0 ,  9 ) . map ( ( article : TSupportArticle )  =>  ( 
202258										< ArticleCard 
203259											key = { article . slug } 
204260											article = { article } 
0 commit comments