Skip to content

Commit 25007be

Browse files
Merge pull request #331 from CivicDataLab/dev
Add collaborative support
2 parents a6d5bf9 + 66c7ee4 commit 25007be

File tree

33 files changed

+6205
-79
lines changed

33 files changed

+6205
-79
lines changed

app/[locale]/(user)/collaboratives/CollaborativesListingClient.tsx

Lines changed: 400 additions & 0 deletions
Large diffs are not rendered by default.

app/[locale]/(user)/collaboratives/[collaborativeSlug]/CollaborativeDetailsClient.tsx

Lines changed: 532 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { Metadata } from 'next';
2+
import { graphql } from '@/gql';
3+
4+
import { GraphQLPublic } from '@/lib/api';
5+
import { generatePageMetadata } from '@/lib/utils';
6+
import CollaborativeDetailClient from './CollaborativeDetailsClient';
7+
8+
const CollaborativeInfoQuery = graphql(`
9+
query CollaborativeInfo($pk: ID!) {
10+
collaborative(pk: $pk) {
11+
id
12+
title
13+
summary
14+
slug
15+
logo {
16+
path
17+
}
18+
tags {
19+
id
20+
value
21+
}
22+
}
23+
}
24+
`);
25+
26+
export async function generateMetadata({
27+
params,
28+
}: {
29+
params: { collaborativeSlug: string };
30+
}): Promise<Metadata> {
31+
try {
32+
const data = await GraphQLPublic(CollaborativeInfoQuery, {}, { pk: params.collaborativeSlug });
33+
const Collaborative = data?.collaborative;
34+
35+
return generatePageMetadata({
36+
title: `${Collaborative?.title} | Collaborative Data | CivicDataSpace`,
37+
description:
38+
Collaborative?.summary ||
39+
`Explore open data and curated datasets in the ${Collaborative?.title} collaborative.`,
40+
keywords: Collaborative?.tags?.map((tag: any) => tag.value) || [],
41+
openGraph: {
42+
type: 'article',
43+
locale: 'en_US',
44+
url: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/collaboratives/${params.collaborativeSlug}`,
45+
title: `${Collaborative?.title} | Collaborative Data | CivicDataSpace`,
46+
description:
47+
Collaborative?.summary ||
48+
`Explore open data and curated datasets in the ${Collaborative?.title} collaborative.`,
49+
siteName: 'CivicDataSpace',
50+
image: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/og.png`,
51+
},
52+
});
53+
} catch (error) {
54+
// Fallback to generic metadata if the API call fails
55+
return generatePageMetadata({
56+
title: `Collaborative Details | CivicDataSpace`,
57+
description: `Explore open data and curated datasets in this collaborative.`,
58+
keywords: ['collaborative', 'data', 'civic', 'open data'],
59+
openGraph: {
60+
type: 'article',
61+
locale: 'en_US',
62+
url: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/collaboratives/${params.collaborativeSlug}`,
63+
title: `Collaborative Details | CivicDataSpace`,
64+
description: `Explore open data and curated datasets in this collaborative.`,
65+
siteName: 'CivicDataSpace',
66+
image: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/og.png`,
67+
},
68+
});
69+
}
70+
}
71+
72+
export default function Page() {
73+
return <CollaborativeDetailClient />;
74+
}
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
'use client';
2+
3+
import React, { useState } from 'react';
4+
import Image from 'next/image';
5+
import { Button, Icon, Spinner, Tag, Text, Tray } from 'opub-ui';
6+
import ReactMarkdown from 'react-markdown';
7+
import rehypeRaw from 'rehype-raw';
8+
import remarkGfm from 'remark-gfm';
9+
10+
import { Icons } from '@/components/icons';
11+
import Metadata from './Metadata';
12+
13+
const PrimaryDetails = ({ data, isLoading }: { data: any; isLoading: any }) => {
14+
const [open, setOpen] = useState(false);
15+
16+
return (
17+
<div>
18+
<div>
19+
<Text variant="heading2xl" color="onBgDefault">
20+
{data.collaborativeBySlug.title}
21+
</Text>
22+
</div>
23+
<div className="mt-4 flex flex-wrap gap-2">
24+
{data.collaborativeBySlug.tags.map((item: any, index: number) => (
25+
<div key={index}>
26+
<Tag
27+
fillColor="var(--accent-tertiary-color)"
28+
borderColor="#5C9A91"
29+
textColor="black"
30+
>
31+
{item.value}
32+
</Tag>
33+
</div>
34+
))}
35+
</div>
36+
<div
37+
className="mt-6 flex sm:block md:block lg:hidden"
38+
title="About the Collaborative"
39+
>
40+
<Tray
41+
size="narrow"
42+
open={open}
43+
onOpenChange={setOpen}
44+
trigger={
45+
<div>
46+
<Button
47+
kind="tertiary"
48+
className="lg:hidden"
49+
onClick={(e) => setOpen(true)}
50+
>
51+
<div className="flex items-center gap-2 py-2">
52+
<Icon source={Icons.info} size={24} color="default" />
53+
<Text color="onBgDefault">Metadata</Text>
54+
</div>
55+
</Button>
56+
</div>
57+
}
58+
>
59+
{isLoading ? (
60+
<div className=" mt-8 flex justify-center">
61+
<Spinner />
62+
</div>
63+
) : (
64+
<Metadata data={data} setOpen={setOpen} />
65+
)}
66+
</Tray>
67+
</div>
68+
{data.collaborativeBySlug.coverImage && (
69+
<div className="mt-6 lg:mt-10">
70+
<Image
71+
src={`${process.env.NEXT_PUBLIC_BACKEND_URL}/${data.collaborativeBySlug.coverImage?.path.replace('/code/files/', '')}`}
72+
alt={data.collaborativeBySlug.title}
73+
width={1200}
74+
height={400}
75+
className="h-auto w-full rounded-2 object-cover"
76+
unoptimized
77+
/>
78+
</div>
79+
)}
80+
81+
{/* Stats Section */}
82+
<div className="mt-10 flex flex-wrap items-center gap-8 lg:mt-12 lg:gap-0">
83+
<div className="flex flex-col border-x-[1px] border-solid border-tertiaryAccent px-8">
84+
<Text variant="heading3xl" className="text-secondaryOrange">
85+
{data.collaborativeBySlug.useCases?.length || 0}
86+
</Text>
87+
<Text variant="bodyLg" color="onBgDefault" className="w-24">
88+
Use Cases
89+
</Text>
90+
</div>
91+
92+
<div className="flex flex-col border-x-[1px] border-solid border-tertiaryAccent px-8">
93+
<Text variant="heading3xl" className="text-secondaryOrange">
94+
{data.collaborativeBySlug.datasets?.length || 0}
95+
</Text>
96+
<Text variant="bodyLg" color="onBgDefault" className="w-24">
97+
Datasets
98+
</Text>
99+
</div>
100+
101+
<div className="flex flex-col border-x-[1px] border-solid border-tertiaryAccent px-8">
102+
<Text variant="heading3xl" className="text-secondaryOrange">
103+
{(data.collaborativeBySlug.supportingOrganizations?.length || 0) +
104+
(data.collaborativeBySlug.partnerOrganizations?.length || 0)}
105+
</Text>
106+
<Text variant="bodyLg" color="onBgDefault" className="w-24">
107+
Organizations
108+
</Text>
109+
</div>
110+
111+
<div className="flex flex-col border-x-[1px] border-solid border-tertiaryAccent px-8">
112+
<Text variant="heading3xl" className="text-secondaryOrange">
113+
{data.collaborativeBySlug.contributors?.length || 0}
114+
</Text>
115+
<Text variant="bodyLg" color="onBgDefault" className="w-24">
116+
Contributors
117+
</Text>
118+
</div>
119+
</div>
120+
121+
<div className=" lg:pr-4">
122+
{data.collaborativeBySlug.geographies &&
123+
data.collaborativeBySlug.geographies.length > 0 && (
124+
<div className="mt-6 lg:mt-10">
125+
<Text variant="headingXl" color="onBgDefault">
126+
Geographies
127+
</Text>
128+
<div className="mt-4 flex flex-wrap gap-2">
129+
{data.collaborativeBySlug.geographies.map(
130+
(geo: any, index: number) => (
131+
<Tag
132+
key={index}
133+
fillColor="var(--orange-secondary-color)"
134+
borderColor="var(--orange-secondary-text)"
135+
textColor="black"
136+
>
137+
{geo.name}
138+
</Tag>
139+
)
140+
)}
141+
</div>
142+
</div>
143+
)}
144+
<div className="mt-6 lg:mt-10">
145+
<Text variant="headingXl" color="onBgDefault">
146+
Summary
147+
</Text>
148+
<div className="prose-h1:text-3xl prose-h2:text-2xl prose-h3:text-xl prose-p:leading-relaxed prose-a:text-blue-400 hover:prose-a:text-blue-300 prose-code:bg-gray-800 prose-code:rounded prose-pre:bg-gray-900 prose-pre:border prose-pre:border-gray-700 prose-blockquote:border-l-blue-500 prose-th:bg-gray-800 prose-img:rounded-lg prose prose-lg prose-invert mt-4 max-w-none prose-headings:text-white prose-p:text-white prose-a:underline prose-blockquote:text-white prose-strong:text-white prose-em:text-white prose-code:px-1 prose-code:py-0.5 prose-code:text-white prose-code:before:content-none prose-code:after:content-none prose-pre:text-white prose-ol:text-white prose-ul:text-white prose-li:text-white prose-li:marker:text-white prose-table:text-white prose-thead:border-white prose-tr:border-white prose-th:border-white prose-th:text-white prose-td:border-white prose-td:text-white prose-hr:border-white">
149+
<ReactMarkdown
150+
remarkPlugins={[remarkGfm]}
151+
rehypePlugins={[rehypeRaw]}
152+
>
153+
{data.collaborativeBySlug.summary}
154+
</ReactMarkdown>
155+
</div>
156+
</div>
157+
</div>
158+
</div>
159+
);
160+
};
161+
162+
export default PrimaryDetails;

0 commit comments

Comments
 (0)