@@ -10,8 +10,10 @@ import { GraphQLPublic } from '@/lib/api';
1010import { formatDate , generateJsonLd } from '@/lib/utils' ;
1111import { useQuery } from '@tanstack/react-query' ;
1212import Image from 'next/image' ;
13- import { Button , Card , Icon , Text } from 'opub-ui' ;
13+ import { Button , Card , Icon , SearchInput , Select , Text } from 'opub-ui' ;
1414import { useState } from 'react' ;
15+ import { cn } from '@/lib/utils' ;
16+ import Styles from '../datasets/dataset.module.scss' ;
1517
1618const PublishedCollaboratives = graphql ( `
1719 query PublishedCollaboratives {
@@ -57,6 +59,10 @@ const PublishedCollaboratives = graphql(`
5759 code
5860 name
5961 }
62+ geographies {
63+ id
64+ name
65+ }
6066 datasetCount
6167 metadata {
6268 metadataItem {
@@ -73,7 +79,7 @@ const PublishedCollaboratives = graphql(`
7379
7480const CollaborativesListingClient = ( ) => {
7581 const [ searchTerm , setSearchTerm ] = useState ( '' ) ;
76- const [ selectedSector , setSelectedSector ] = useState ( '' ) ;
82+ const [ sortBy , setSortBy ] = useState ( 'title_asc ' ) ;
7783
7884 const {
7985 data : collaborativesData ,
@@ -107,20 +113,30 @@ const CollaborativesListingClient = () => {
107113
108114 const collaboratives = collaborativesData ?. publishedCollaboratives || [ ] ;
109115
110- // Filter collaboratives based on search term and sector
111- const filteredCollaboratives = collaboratives . filter ( ( collaborative ) => {
112- const matchesSearch = collaborative . title ?. toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) ) ||
113- collaborative . summary ?. toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) ) ;
114- const matchesSector = ! selectedSector ||
115- collaborative . sectors ?. some ( sector => sector . name === selectedSector ) ;
116- return matchesSearch && matchesSector ;
117- } ) ;
118-
119- // Get unique sectors for filter dropdown
120- const allSectors = collaboratives . flatMap ( ( collaborative : TypeCollaborative ) =>
121- collaborative . sectors ?. map ( ( sector : any ) => sector . name ) || [ ]
122- ) ;
123- const uniqueSectors = [ ...new Set ( allSectors ) ] ;
116+ // Filter and sort collaboratives
117+ const filteredAndSortedCollaboratives = collaboratives
118+ . filter ( ( collaborative ) => {
119+ const matchesSearch = collaborative . title ?. toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) ) ||
120+ collaborative . summary ?. toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) ) ;
121+ return matchesSearch ;
122+ } )
123+ . sort ( ( a , b ) => {
124+ const [ field , direction ] = sortBy . split ( '_' ) ;
125+
126+ if ( field === 'title' ) {
127+ const comparison = ( a . title || '' ) . localeCompare ( b . title || '' ) ;
128+ return direction === 'asc' ? comparison : - comparison ;
129+ } else if ( field === 'startedOn' ) {
130+ const dateA = new Date ( a . startedOn || 0 ) . getTime ( ) ;
131+ const dateB = new Date ( b . startedOn || 0 ) . getTime ( ) ;
132+ return direction === 'asc' ? dateA - dateB : dateB - dateA ;
133+ } else if ( field === 'datasetCount' ) {
134+ const countA = a . datasetCount || 0 ;
135+ const countB = b . datasetCount || 0 ;
136+ return direction === 'asc' ? countA - countB : countB - countA ;
137+ }
138+ return 0 ;
139+ } ) ;
124140 const jsonLd = generateJsonLd ( {
125141 '@context' : 'https://schema.org' ,
126142 '@type' : 'WebPage' ,
@@ -181,31 +197,65 @@ const CollaborativesListingClient = () => {
181197 < div className = "container py-8 lg:py-14" >
182198 { /* Header Section */ }
183199 < div className = "mb-8" >
200+ < Text variant = "heading2xl" fontWeight = "bold" className = "mb-8" >
201+ Explore Collaboratives
202+ </ Text >
184203
185204 { /* Search and Filter Section */ }
186- < div className = "flex flex-col gap-4 md:flex-row md:items-center md:gap-6" >
187- < div className = "flex-1" >
188- < input
189- type = "text"
190- placeholder = "Search collaboratives..."
191- value = { searchTerm }
192- onChange = { ( e ) => setSearchTerm ( e . target . value ) }
193- className = "w-full rounded-lg border border-greyExtralight px-4 py-2 focus:border-primaryBlue focus:outline-none"
194- />
195- </ div >
196- < div className = "md:w-48" >
197- < select
198- value = { selectedSector }
199- onChange = { ( e ) => setSelectedSector ( e . target . value ) }
200- className = "w-full rounded-lg border border-greyExtralight px-4 py-2 focus:border-primaryBlue focus:outline-none"
205+ < div className = "flex flex-wrap gap-6 lg:flex-nowrap" >
206+ < SearchInput
207+ label = { '' }
208+ className = { cn ( 'w-full' , Styles . Search ) }
209+ onSubmit = { ( e ) => {
210+ setSearchTerm ( e ) ;
211+ } }
212+ onClear = { ( ) => {
213+ setSearchTerm ( '' ) ;
214+ } }
215+ name = { 'Start typing to search for any collaborative' }
216+ />
217+ < div className = "flex items-center gap-2" >
218+ < Text
219+ variant = "bodyLg"
220+ fontWeight = "semibold"
221+ className = "whitespace-nowrap text-secondaryOrange"
201222 >
202- < option value = "" > All Sectors</ option >
203- { uniqueSectors . map ( ( sector : string ) => (
204- < option key = { sector } value = { sector } >
205- { sector }
206- </ option >
207- ) ) }
208- </ select >
223+ Sort :
224+ </ Text >
225+ < Select
226+ label = ""
227+ labelInline
228+ name = "sort-select"
229+ options = { [
230+ {
231+ label : 'Title Asc' ,
232+ value : 'title_asc' ,
233+ } ,
234+ {
235+ label : 'Title Desc' ,
236+ value : 'title_desc' ,
237+ } ,
238+ {
239+ label : 'Started Date Asc' ,
240+ value : 'startedOn_asc' ,
241+ } ,
242+ {
243+ label : 'Started Date Desc' ,
244+ value : 'startedOn_desc' ,
245+ } ,
246+ {
247+ label : 'Dataset Count Asc' ,
248+ value : 'datasetCount_asc' ,
249+ } ,
250+ {
251+ label : 'Dataset Count Desc' ,
252+ value : 'datasetCount_desc' ,
253+ } ,
254+ ] }
255+ onChange = { ( e : any ) => {
256+ setSortBy ( e ) ;
257+ } }
258+ />
209259 </ div >
210260 </ div >
211261 </ div >
@@ -228,16 +278,10 @@ const CollaborativesListingClient = () => {
228278 { /* Results Section */ }
229279 { ! isLoading && ! error && (
230280 < >
231- < div className = "mb-6 flex items-center justify-between" >
232- < Text variant = "headingLg" >
233- { filteredCollaboratives . length } Collaborative{ filteredCollaboratives . length !== 1 ? 's' : '' } Found
234- </ Text >
235- </ div >
236-
237281 { /* Collaboratives Grid */ }
238- { filteredCollaboratives . length > 0 ? (
282+ { filteredAndSortedCollaboratives . length > 0 ? (
239283 < div className = "grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3" >
240- { filteredCollaboratives . map ( ( collaborative : TypeCollaborative ) => (
284+ { filteredAndSortedCollaboratives . map ( ( collaborative : TypeCollaborative ) => (
241285 < Card
242286 key = { collaborative . id }
243287 title = { collaborative . title || '' }
@@ -258,10 +302,9 @@ const CollaborativesListingClient = () => {
258302 icon : Icons . globe ,
259303 label : 'Geography' ,
260304 value :
261- collaborative . metadata ?. find (
262- ( meta : any ) =>
263- meta . metadataItem ?. label === 'Geography'
264- ) ?. value || 'N/A' ,
305+ collaborative . geographies && collaborative . geographies . length > 0
306+ ? collaborative . geographies . map ( ( geo : any ) => geo . name ) . join ( ', ' )
307+ : 'N/A' ,
265308 } ,
266309 ] }
267310 href = { `/collaboratives/${ collaborative . slug } ` }
@@ -296,15 +339,14 @@ const CollaborativesListingClient = () => {
296339 < Text variant = "bodyLg" color = "subdued" >
297340 Try adjusting your search terms or filters.
298341 </ Text >
299- { ( searchTerm || selectedSector ) && (
342+ { searchTerm && (
300343 < Button
301344 onClick = { ( ) => {
302345 setSearchTerm ( '' ) ;
303- setSelectedSector ( '' ) ;
304346 } }
305347 kind = "secondary"
306348 >
307- Clear Filters
349+ Clear Search
308350 </ Button >
309351 ) }
310352 </ div >
0 commit comments