diff --git a/app/[locale]/(user)/collaboratives/CollaborativesListingClient.tsx b/app/[locale]/(user)/collaboratives/CollaborativesListingClient.tsx new file mode 100644 index 00000000..6474ac41 --- /dev/null +++ b/app/[locale]/(user)/collaboratives/CollaborativesListingClient.tsx @@ -0,0 +1,400 @@ +'use client'; + +import BreadCrumbs from '@/components/BreadCrumbs'; +import { Icons } from '@/components/icons'; +import JsonLd from '@/components/JsonLd'; +import { Loading } from '@/components/loading'; +import { graphql } from '@/gql'; +import { TypeCollaborative } from '@/gql/generated/graphql'; +import { GraphQLPublic } from '@/lib/api'; +import { formatDate, generateJsonLd } from '@/lib/utils'; +import { useQuery } from '@tanstack/react-query'; +import Image from 'next/image'; +import { Button, Card, Icon, SearchInput, Select, Text } from 'opub-ui'; +import { useState } from 'react'; +import { cn } from '@/lib/utils'; +import Styles from '../datasets/dataset.module.scss'; + +// Helper function to strip markdown and HTML tags for card preview +const stripMarkdown = (markdown: string): string => { + if (!markdown) return ''; + return markdown + // Remove code blocks first (before other replacements) + .replace(/```[\s\S]*?```/g, '') + // Remove inline code + .replace(/`([^`]+)`/g, '$1') + // Remove images + .replace(/!\[([^\]]*)\]\([^)]+\)/g, '$1') + // Remove links + .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') + // Remove headers + .replace(/^#{1,6}\s+/gm, '') + // Remove bold + .replace(/\*\*([^*]+)\*\*/g, '$1') + .replace(/__([^_]+)__/g, '$1') + // Remove italic + .replace(/\*([^*]+)\*/g, '$1') + .replace(/_([^_]+)_/g, '$1') + // Remove strikethrough + .replace(/~~([^~]+)~~/g, '$1') + // Remove blockquotes + .replace(/^\s*>\s+/gm, '') + // Remove horizontal rules + .replace(/^(-{3,}|_{3,}|\*{3,})$/gm, '') + // Remove list markers + .replace(/^\s*[-*+]\s+/gm, '') + .replace(/^\s*\d+\.\s+/gm, '') + // Remove HTML tags + .replace(/<[^>]*>/g, '') + // Remove extra whitespace and newlines + .replace(/\n\s*\n/g, '\n') + .replace(/\n/g, ' ') + .replace(/\s+/g, ' ') + .trim(); +}; + +const PublishedCollaboratives = graphql(` + query PublishedCollaboratives { + publishedCollaboratives { + id + title + summary + slug + created + startedOn + completedOn + status + isIndividualCollaborative + user { + fullName + id + profilePicture { + url + } + } + organization { + name + slug + id + logo { + url + } + } + logo { + name + path + } + tags { + id + value + } + sectors { + id + name + } + sdgs { + id + code + name + } + geographies { + id + name + } + datasetCount + metadata { + metadataItem { + id + label + dataType + } + id + value + } + } + } +`); + +const CollaborativesListingClient = () => { + const [searchTerm, setSearchTerm] = useState(''); + const [sortBy, setSortBy] = useState('title_asc'); + + const { + data: collaborativesData, + isLoading, + error, + } = useQuery<{ publishedCollaboratives: TypeCollaborative[] }>( + ['fetch_published_collaboratives'], + async () => { + console.log('Fetching collaboratives...'); + try { + // @ts-expect-error - Query has no variables + const result = await GraphQLPublic( + PublishedCollaboratives as any, + {} + ); + console.log('Collaboratives result:', result); + return result as { publishedCollaboratives: TypeCollaborative[] }; + } catch (err) { + console.error('Error fetching collaboratives:', err); + throw err; + } + }, + { + refetchOnMount: true, + refetchOnReconnect: true, + retry: (failureCount) => { + return failureCount < 3; + }, + } + ); + + const collaboratives = collaborativesData?.publishedCollaboratives || []; + + // Filter and sort collaboratives + const filteredAndSortedCollaboratives = collaboratives + .filter((collaborative) => { + const matchesSearch = collaborative.title?.toLowerCase().includes(searchTerm.toLowerCase()) || + collaborative.summary?.toLowerCase().includes(searchTerm.toLowerCase()); + return matchesSearch; + }) + .sort((a, b) => { + const [field, direction] = sortBy.split('_'); + + if (field === 'title') { + const comparison = (a.title || '').localeCompare(b.title || ''); + return direction === 'asc' ? comparison : -comparison; + } else if (field === 'startedOn') { + const dateA = new Date(a.startedOn || 0).getTime(); + const dateB = new Date(b.startedOn || 0).getTime(); + return direction === 'asc' ? dateA - dateB : dateB - dateA; + } else if (field === 'datasetCount') { + const countA = a.datasetCount || 0; + const countB = b.datasetCount || 0; + return direction === 'asc' ? countA - countB : countB - countA; + } + return 0; + }); + const jsonLd = generateJsonLd({ + '@context': 'https://schema.org', + '@type': 'WebPage', + name: 'CivicDataLab', + url: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/collaboratives`, + description: + 'Explore collaborative data initiatives and partnerships that bring organizations together to create impactful solutions.', + }); + return ( +
+ + + <> + <> +
+
+
+
+ + Our Collaboratives + + + By Collaboratives we mean a collective effort by several organisations + in any specific sectors that can be applied to address some of the + most pressing concerns from hyper-local to the global level simultaneously. + +
+
+ {'collaborative'} +
+
+
+
+ + + +
+
+ {/* Header Section */} +
+ + Explore Collaboratives + + + {/* Search and Filter Section */} +
+ { + setSearchTerm(e); + }} + onClear={() => { + setSearchTerm(''); + }} + name={'Start typing to search for any collaborative'} + /> +
+ + Sort : + +