Skip to content

Commit cf9505e

Browse files
authored
Merge pull request #288 from CivicDataLab/284-seo-setup
Add Metadata tags in consumer pages
2 parents 3fb561b + c9f81a7 commit cf9505e

File tree

21 files changed

+1360
-1002
lines changed

21 files changed

+1360
-1002
lines changed

app/[locale]/(user)/about-us/page.tsx

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,29 @@ import Team from './components/Team';
77

88
export const generateMetadata = () =>
99
generatePageMetadata({
10-
title: 'About Us',
10+
title: 'About CivicDataSpace | Empowering Public Good with Open Data',
1111
description:
12-
'Learn more about CivicDataSpace, our mission, and the people behind it.',
13-
keywords: ['CivicDataSpace', 'About Us', 'Open Data', 'CDL'],
12+
'Learn about CivicDataSpace — an open-source platform built to foster inclusive, interoperable, and AI-ready data ecosystems for public good.',
13+
keywords: [
14+
'CivicDataSpace',
15+
'About CivicDataSpace',
16+
'Open Data',
17+
'CivicTech',
18+
'Data for Public Good',
19+
'Inclusive Data',
20+
'AI-ready Data',
21+
'CivicDataLab',
22+
'Open Source Platform',
23+
],
1424
openGraph: {
1525
type: 'website',
1626
locale: 'en_US',
17-
url: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/about`, // update if dynamic
18-
title: 'About CivicDataSpace',
27+
url: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/about`,
28+
title: 'About CivicDataSpace | Empowering Public Good with Open Data',
1929
description:
20-
'Explore the mission, vision, and people powering CivicDataSpace an open-source platform to enable data for public good.',
30+
'Explore the mission, vision, and team behind CivicDataSpace an open-source initiative to unlock the power of data for civic impact.',
2131
siteName: 'CivicDataSpace',
22-
image: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/og.png`, // from /public/og.png
32+
image: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/og.png`,
2333
},
2434
});
2535

app/[locale]/(user)/components/ListingComponent.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client'
2+
13
import React, { useEffect, useReducer, useRef, useState } from 'react';
24
import Image from 'next/image';
35
import { useRouter } from 'next/navigation';
@@ -20,6 +22,7 @@ import { Icons } from '@/components/icons';
2022
import { Loading } from '@/components/loading';
2123
import Filter from '../datasets/components/FIlter/Filter';
2224
import Styles from '../datasets/dataset.module.scss';
25+
import { fetchData } from '@/fetch';
2326

2427
// Interfaces
2528
interface Bucket {
@@ -185,11 +188,7 @@ const useUrlParams = (
185188

186189
// Listing Component Props
187190
interface ListingProps {
188-
fetchDatasets: (variables: string) => Promise<{
189-
results: any[];
190-
total: number;
191-
aggregations: Aggregations;
192-
}>;
191+
type: string;
193192
breadcrumbData?: { href: string; label: string }[];
194193
headerComponent?: React.ReactNode;
195194
categoryName?: string;
@@ -200,7 +199,7 @@ interface ListingProps {
200199
}
201200

202201
const ListingComponent: React.FC<ListingProps> = ({
203-
fetchDatasets,
202+
type,
204203
breadcrumbData,
205204
headerComponent,
206205
categoryName,
@@ -229,7 +228,7 @@ const ListingComponent: React.FC<ListingProps> = ({
229228
if (variables) {
230229
const currentFetchId = ++latestFetchId.current;
231230

232-
fetchDatasets(variables)
231+
fetchData(type,variables)
233232
.then((res) => {
234233
// Only set if this is the latest call
235234
if (currentFetchId === latestFetchId.current) {
@@ -240,7 +239,7 @@ const ListingComponent: React.FC<ListingProps> = ({
240239
console.error(err);
241240
});
242241
}
243-
}, [variables, fetchDatasets]);
242+
}, [variables, type]);
244243

245244
const [hasMounted, setHasMounted] = useState(false);
246245

app/[locale]/(user)/datasets/[datasetIdentifier]/page.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ const datasetMetaQuery: any = graphql(`
1010
title
1111
description
1212
id
13+
tags {
14+
id
15+
value
16+
}
1317
}
1418
}
1519
`);
@@ -27,16 +31,16 @@ export async function generateMetadata({
2731
);
2832

2933
const dataset = res?.getDataset;
30-
3134
return generatePageMetadata({
32-
title: dataset?.title,
35+
title: `${dataset?.title} | Dataset | CivicDataSpace`,
3336
description: dataset?.description,
37+
keywords: dataset?.tags?.map((tag: any) => tag.value) || [],
3438
openGraph: {
3539
type: 'dataset',
3640
locale: 'en_US',
3741
url: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/datasets/${params.datasetIdentifier}`,
3842
title: dataset?.title,
39-
description: dataset?.description,
43+
description: dataset?.description,
4044
siteName: 'CivicDataSpace',
4145
image: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/og.png`,
4246
},
Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,33 @@
1-
'use client';
2-
31
import React from 'react';
4-
import { fetchDatasets } from '@/fetch';
2+
3+
import { generatePageMetadata } from '@/lib/utils';
54
import ListingComponent from '../components/ListingComponent';
65

6+
export const generateMetadata = () =>
7+
generatePageMetadata({
8+
title: 'Browse Open Datasets | CivicDataSpace',
9+
description:
10+
'Discover and explore a comprehensive collection of open datasets for research, policy-making, and civic innovation. Filter by sector, format.',
11+
keywords: [
12+
'Open Datasets',
13+
'Data Discovery',
14+
'Public Data',
15+
'Research Data',
16+
'Civic Data',
17+
'Dataset Search',
18+
],
19+
openGraph: {
20+
type: 'website',
21+
locale: 'en_US',
22+
url: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/datasets`,
23+
title: 'Browse Open Datasets | CivicDataSpace',
24+
description:
25+
'Explore thousands of open datasets across sectors like health, education, governance, and environment on CivicDataSpace',
26+
siteName: 'CivicDataSpace',
27+
image: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/og.png`, // from /public/og.png
28+
},
29+
});
30+
731
const DatasetsListing = () => {
832
const breadcrumbData = [
933
{ href: '/', label: 'Home' },
@@ -12,12 +36,12 @@ const DatasetsListing = () => {
1236

1337
return (
1438
<ListingComponent
15-
fetchDatasets={fetchDatasets}
39+
type="dataset"
1640
breadcrumbData={breadcrumbData}
1741
redirectionURL={`/datasets`}
1842
placeholder="Start typing to search for any Dataset"
1943
/>
2044
);
2145
};
2246

23-
export default DatasetsListing;
47+
export default DatasetsListing;

app/[locale]/(user)/page.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@ export const generateMetadata = () =>
88
generatePageMetadata({
99
title: 'CivicDataSpace – Empowering Public Good with Open Data',
1010
description:
11-
'CivicDataSpace is an open-source platform enabling inclusive and AI-ready data collaboratives. Explore datasets, use cases, and insights for public good.',
11+
'CivicDataSpace is an open-source platform enabling inclusive and AI-ready data collaborative. Explore datasets, use cases, and insights for public good.',
1212
keywords: [
1313
'CivicDataSpace',
1414
'Open Data',
15-
'Data Collaboratives',
15+
'Data Collaborative',
1616
'Public Datasets',
1717
'AI-ready data',
1818
'CivicTech',
1919
'CivicDataLab',
20-
],
20+
],
2121
openGraph: {
2222
type: 'website',
2323
locale: 'en_US',
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
'use client';
2+
3+
import { useState } from 'react';
4+
import Image from 'next/image';
5+
import { graphql } from '@/gql';
6+
import { useQuery } from '@tanstack/react-query';
7+
import { Button, ButtonGroup, Spinner, Text } from 'opub-ui';
8+
9+
import { GraphQL } from '@/lib/api';
10+
import { cn } from '@/lib/utils';
11+
import BreadCrumbs from '@/components/BreadCrumbs';
12+
import PublisherCard from './PublisherCard';
13+
14+
const getAllPublishers: any = graphql(`
15+
query PublishersList {
16+
getPublishers {
17+
__typename
18+
... on TypeOrganization {
19+
name
20+
id
21+
description
22+
logo {
23+
url
24+
}
25+
membersCount
26+
publishedUseCasesCount
27+
publishedDatasetsCount
28+
}
29+
... on TypeUser {
30+
fullName
31+
id
32+
bio
33+
profilePicture {
34+
url
35+
}
36+
publishedUseCasesCount
37+
publishedDatasetsCount
38+
}
39+
}
40+
}
41+
`);
42+
43+
const PublishersListingPage = () => {
44+
const [type, setType] = useState<'all' | 'org' | 'pub'>('all');
45+
const Details: {
46+
data: any;
47+
isLoading: boolean;
48+
isError: boolean;
49+
refetch: any;
50+
} = useQuery(['publishers_list_page'], () =>
51+
GraphQL(getAllPublishers, {}, [])
52+
);
53+
54+
type PublisherType = 'all' | 'org' | 'pub';
55+
const publisherButtons: { key: PublisherType; label: string }[] = [
56+
{ key: 'all', label: 'All Publishers' },
57+
{ key: 'org', label: 'Organizations' },
58+
{ key: 'pub', label: 'Individual Publishers' },
59+
];
60+
61+
const filteredPublishers = Details?.data?.getPublishers?.filter(
62+
(publisher: any) => {
63+
if (type === 'all') return true;
64+
if (type === 'pub') return publisher.__typename === 'TypeUser';
65+
if (type === 'org') return publisher.__typename === 'TypeOrganization';
66+
return false;
67+
}
68+
);
69+
70+
return (
71+
<main >
72+
<BreadCrumbs
73+
data={[
74+
{ href: '/', label: 'Home' },
75+
{ href: '#', label: 'Publishers' },
76+
]}
77+
/>
78+
<>
79+
<>
80+
<div className="w-full">
81+
<div className=" bg-primaryBlue">
82+
<div className=" container flex flex-col-reverse items-center gap-8 py-10 lg:flex-row ">
83+
<div className="flex flex-col gap-5 ">
84+
<Text
85+
variant="heading2xl"
86+
fontWeight="bold"
87+
color="onBgDefault"
88+
>
89+
Our Publishers{' '}
90+
</Text>
91+
<Text
92+
variant="headingLg"
93+
color="onBgDefault"
94+
fontWeight="regular"
95+
className=" leading-3 lg:leading-5"
96+
>
97+
Meet the data providers powering CivicDataSpace — explore
98+
individual and organizational publishers across domains who
99+
are opening up data for impact and transparency.
100+
</Text>
101+
</div>
102+
<div className="flex items-center justify-center gap-2 px-3 ">
103+
<Image
104+
src={'/s2.png'}
105+
alt={'s1'}
106+
width={130}
107+
height={100}
108+
className="h-auto w-[80px] sm:w-[100px] md:w-[120px] lg:w-[130px]"
109+
priority
110+
/>
111+
<Image
112+
src={'/s4.png'}
113+
alt={'s1'}
114+
width={232}
115+
height={100}
116+
className="h-auto w-[140px] sm:w-[180px] md:w-[200px] lg:w-[230px]"
117+
priority
118+
/>
119+
<Image
120+
src={'/s1.png'}
121+
alt={'s1'}
122+
width={130}
123+
height={100}
124+
className="h-auto w-[80px] sm:w-[100px] md:w-[120px] lg:w-[130px]"
125+
priority
126+
/>
127+
</div>
128+
</div>
129+
</div>
130+
131+
<div className="container flex flex-col gap-4 py-10 lg:gap-6">
132+
<Text variant="heading2xl" fontWeight="bold">
133+
Explore Publishers
134+
</Text>
135+
<div>
136+
<ButtonGroup>
137+
<div className="flex flex-wrap gap-4">
138+
{publisherButtons.map((btn) => (
139+
<Button
140+
key={btn.key}
141+
onClick={() => setType(btn.key)}
142+
className={cn(
143+
' w-72 rounded-full py-3',
144+
type === btn.key
145+
? 'bg-tertiaryAccent'
146+
: 'border-1 border-solid border-tertiaryAccent bg-surfaceDefault'
147+
)}
148+
>
149+
<Text variant="headingLg" fontWeight="semibold">
150+
{btn.label}
151+
</Text>
152+
</Button>
153+
))}
154+
</div>
155+
</ButtonGroup>
156+
</div>
157+
{Details.isLoading ? (
158+
<div className="m-4 flex justify-center">
159+
<Spinner />
160+
</div>
161+
) : (
162+
Details.data &&
163+
Details.data.getPublishers.length > 0 && (
164+
<PublisherCard data={filteredPublishers} />
165+
)
166+
)}
167+
</div>
168+
</div>
169+
</>
170+
</>
171+
</main>
172+
);
173+
};
174+
175+
export default PublishersListingPage;

0 commit comments

Comments
 (0)