Skip to content

Commit 4b3c38b

Browse files
authored
Merge pull request #1377 from datopian/feature/ckan-page
Implement landing page for CKAN integration.
2 parents ef86543 + 825895a commit 4b3c38b

File tree

6 files changed

+446
-0
lines changed

6 files changed

+446
-0
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { Player } from '@lottiefiles/react-lottie-player'
2+
import { H2, H3 } from '../custom/header'
3+
import { useTheme } from 'next-themes'
4+
5+
export const CommonUseCases = () => {
6+
const { theme } = useTheme()
7+
const features = [
8+
{
9+
title: 'Government Open Data Portals',
10+
description:
11+
'Deliver cost-effective, standards-compliant open data sites. Publish datasets, maps, and reports easily, while ensuring interoperability through DCAT, Dublin Core, and other open-data schemas.',
12+
icon: `/static/icons/${theme}/data-portal.json`,
13+
style: 'dark:-rotate-[4deg]',
14+
},
15+
{
16+
title: 'Research Data Repositories',
17+
description:
18+
'Designed for academic and scientific domains. Supports DOI minting, adherence to FAIR principles (Findable, Accessible, Interoperable, Reusable), integrated metadata indexing, and interactive data previews.',
19+
icon: `/static/icons/${theme}/data-catalog.json`,
20+
style: 'dark:-rotate-[4deg]',
21+
},
22+
{
23+
title: 'Enterprise DataHubs',
24+
description:
25+
'Built for internal and partner data sharing with full deployment flexibility – host on-premises or in any cloud. Maintain complete IP ownership and enforce enterprise-grade security, access control, and audit trails.',
26+
icon: `/static/icons/${theme}/analytical-skill.json`,
27+
style: 'dark:-rotate-[4deg]',
28+
},
29+
]
30+
31+
return (
32+
<div className="py-24">
33+
<div className="">
34+
<H2 className="text-center mb-4">Common Use Cases</H2>
35+
<H3 className="text-center opacity-75">
36+
Tailored solutions built with CKAN & PortalJS to meet diverse data publishing needs.
37+
</H3>
38+
<div className="mt-16 grid grid-cols-1 gap-y-12 sm:gap-x-12 lg:grid-cols-3 lg:gap-x-6">
39+
{features.map((feature) => (
40+
<div
41+
key={feature.title}
42+
className="relative flex flex-col rounded-xl dark:bg-slate-900 dark:hover:bg-slate-800 hover:bg-slate-100 transition-all duration-300 ring-1 ring-slate-200 dark:ring-slate-800 p-7 rounded-lg shadow-lg overflow-hidden"
43+
>
44+
<div className="flex-shrink-0 w-full flex items-start -ml-2">
45+
<Player
46+
autoplay
47+
loop
48+
src={feature.icon}
49+
className={`w-14 h-14 ${feature.style}`}
50+
/>
51+
</div>
52+
<div className="pt-4">
53+
<h3 className="text-lg font-medium ">{feature.title}</h3>
54+
<p className="mt-4 text-base text-gray-400">
55+
{feature.description}
56+
</p>
57+
</div>
58+
</div>
59+
))}
60+
</div>
61+
</div>
62+
</div>
63+
)
64+
}

site/components/ckan/Hero.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import React from 'react';
2+
import ButtonLink from '../ButtonLink';
3+
import Image from 'next/image';
4+
5+
export default function Hero() {
6+
return (
7+
<div
8+
className="overflow-hidden -mb-32 mt-[-4.5rem] pb-32 pt-[4.5rem] lg:mt-[-4.75rem] lg:pt-[4.75rem]"
9+
id="hero"
10+
>
11+
<div className="py-16 sm:px-2 lg:relative lg:py-20 lg:px-0">
12+
<div className="mx-auto max-w-2xl px-4 lg:max-w-8xl lg:px-8 xl:px-12">
13+
<div className="relative mb-10 lg:mb-0 text-center">
14+
<h1 className="inline bg-gradient-to-r from-blue-500 via-blue-300 to-blue-500 bg-clip-text text-5xl tracking-tight text-transparent">
15+
CKAN & PortalJS
16+
</h1>
17+
<p className="mt-4 text-xl tracking-tight text-slate-400">
18+
Deliver a modern, decoupled frontend for your CKAN-powered open data portal.
19+
</p>
20+
<ButtonLink
21+
href="/opensource/docs"
22+
title="Read the docs"
23+
className="text-sm mr-2"
24+
>
25+
Read the docs
26+
</ButtonLink>
27+
<ButtonLink
28+
style="secondary"
29+
className="mt-8 text-sm"
30+
href="https://cloud.portaljs.com/auth/signup"
31+
>
32+
Deploy with PortalJS Cloud
33+
</ButtonLink>
34+
</div>
35+
</div>
36+
</div>
37+
</div>
38+
);
39+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import { Player } from '@lottiefiles/react-lottie-player'
2+
import { H2, H3 } from '../custom/header'
3+
import { useTheme } from 'next-themes'
4+
5+
const features = [
6+
{
7+
title: 'Integration with Multiple Data Sources',
8+
description: 'Connect not just to CKAN but to any data catalog or API endpoint, aggregating datasets from diverse origins.',
9+
icon: 'connection',
10+
iconStyle: 'dark:-rotate-[4deg]',
11+
style: 'max-lg:rounded-t-[2rem] lg:rounded-tl-[2rem] w-full',
12+
colSpan: 'lg:col-span-4',
13+
},
14+
{
15+
title: 'Seamless Data Integration',
16+
description: 'Fetch CKAN datasets, resources, and metadata via API with built-in pagination, filtering, and format rendering.',
17+
icon: 'api',
18+
iconStyle: 'dark:-rotate-[2deg]',
19+
style: 'lg:rounded-tr-[2rem]',
20+
colSpan: 'lg:col-span-2',
21+
},
22+
{
23+
title: 'Easier CMS Integration',
24+
description: 'Plug into popular CMS platforms—WordPress, Drupal, Contentful—and manage content alongside your data catalog.',
25+
icon: 'browser',
26+
iconStyle: 'dark:-rotate-[4deg]',
27+
style: '',
28+
colSpan: 'lg:col-span-2',
29+
},
30+
{
31+
title: 'Deployment Flexibility',
32+
description: 'Deploy on Vercel, Cloudflare Pages, Netlify, or bundle for your own AWS, GCP, or Azure environment for optimal DX.',
33+
icon: 'rocket',
34+
iconStyle: 'dark:-rotate-[3deg]',
35+
style: '',
36+
colSpan: 'lg:col-span-4',
37+
},
38+
{
39+
title: 'Optimized Performance',
40+
description: 'Enjoy SSR-friendly React components, lazy-loading, minimal bundle size, and Next.js ISR (Incremental Static Regeneration) for fresh data without full rebuilds.',
41+
icon: 'server',
42+
iconStyle: 'dark:-rotate-[4deg]',
43+
style: '',
44+
colSpan: 'lg:col-span-2',
45+
},
46+
{
47+
title: 'SEO & Accessibility',
48+
description: 'Built-in Next-SEO and WCAG-compliant components ensure your data is discoverable and inclusive.',
49+
icon: 'search2',
50+
iconStyle: 'dark:-rotate-[2deg]',
51+
style: '',
52+
colSpan: 'lg:col-span-2',
53+
},
54+
{
55+
title: 'Developer Experience',
56+
description: 'TypeScript support, CI/CD-ready setup, and comprehensive docs make integration into your workflow a breeze.',
57+
icon: 'server-1',
58+
iconStyle: 'dark:-rotate-[4deg]',
59+
style: 'max-lg:rounded-b-[2rem] lg:rounded-br-[2rem] w-full',
60+
colSpan: 'lg:col-span-2',
61+
},
62+
]
63+
64+
export const KeyFeatures = () => {
65+
66+
const { theme } = useTheme()
67+
return (
68+
<div className="py-24">
69+
<div className="mx-auto">
70+
<div className="flex flex-col items-center">
71+
<H2 className="mt-2 tracking-4 text-center">
72+
Key Features
73+
</H2>
74+
<H3 className="text-lg leading-8 opacity-75 text-center">
75+
Pre-built for CKAN & Open Data—static-first rendering, serverless
76+
scaling, and full WCAG Compliance
77+
</H3>
78+
</div>
79+
<div className="mt-10 grid grid-cols-1 gap-4 sm:mt-16 lg:grid-cols-6 lg:grid-rows-2">
80+
{features.map((feature, index) => (
81+
<div key={index} className={`flex p-px ${feature.colSpan}`}>
82+
<div className={`overflow-hidden rounded-lg dark:bg-slate-900 ring-1 ring-slate-200 dark:ring-slate-800 dark:hover:bg-slate-800 hover:bg-slate-100 transition-all duration-300 shadow-lg ${feature.style}`}>
83+
<div className="flex flex-col items-start p-10">
84+
<Player src={`/static/icons/${theme}/${feature.icon}.json`} autoplay loop className={`w-14 h-14 ${feature.iconStyle}`} />
85+
<p className="mt-2 text-xl font-semibold tracking-tight dark:text-white">
86+
{feature.title}
87+
</p>
88+
<p className="mt-2 ">{feature.description}</p>
89+
</div>
90+
</div>
91+
</div>
92+
))}
93+
</div>
94+
</div>
95+
</div>
96+
)
97+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { useTheme } from 'next-themes'
2+
import Image from 'next/image'
3+
import Link from 'next/link'
4+
5+
export default function SocialProof() {
6+
const logos = [
7+
{
8+
name: 'Transport Data Commons',
9+
srcDark: '/static/img/social-proof/TDC_logo.svg',
10+
srcLight: '/static/img/social-proof/TDC_logo.svg',
11+
url: 'https://portal.transport-data.org/',
12+
style: 'grayscale dark:invert',
13+
width: 240,
14+
},
15+
{
16+
name: 'FIND DXConnect',
17+
srcDark: '/static/img/social-proof/dx-connect-find.svg',
18+
srcLight: '/static/img/social-proof/dx-connect-find.svg',
19+
url: 'https://finddx.portaljs.com/',
20+
style: ' grayscale dark:invert',
21+
width: 170,
22+
},
23+
{
24+
name: 'Scottish & Southern Electricity Networks',
25+
srcDark: '/static/img/social-proof/sse-logo-white.png',
26+
srcLight: '/static/img/social-proof/sse-logo-white.png',
27+
url: 'https://data.ssen.co.uk/',
28+
style: 'max-h-28 grayscale invert dark:invert-0 opacity-75',
29+
width: 200,
30+
},
31+
{
32+
name: 'IDPO (University of Sydney)',
33+
srcDark: '/static/img/social-proof/UNIOFSY.png',
34+
srcLight: '/static/img/social-proof/usyd-dark.svg',
35+
url: 'https://www.idpo.org.au',
36+
style: '',
37+
width: 115,
38+
},
39+
{
40+
name: 'UAE Ministry of Energy and Infrastructure',
41+
srcDark: '/static/img/social-proof/uae_moei_eng-logo.png',
42+
srcLight: '/static/img/social-proof/uae_moei_eng-logo.png',
43+
url: 'https://opendata.moei.gov.ae/',
44+
style: ' grayscale',
45+
width: 230,
46+
},
47+
{
48+
name: 'Marcus Institute',
49+
srcDark:
50+
'/static/img/social-proof/Marcus_Institute_HMS_vertical-grey-transparent.png',
51+
srcLight: '/static/img/social-proof/Marcus_Institute_HMS-light.png',
52+
url: 'https://data.hsl.harvard.edu/',
53+
style: 'grayscale',
54+
width: 200,
55+
},
56+
{
57+
name: 'ODNI',
58+
srcDark: '/static/img/social-proof/Open-Data-Northern-Ireland-grey.png',
59+
srcLight: '/static/img/social-proof/Open-Data-Northern-Ireland-light.png',
60+
url: 'https://www.opendatani.gov.uk/',
61+
style: 'grayscale',
62+
width: 180,
63+
},
64+
{
65+
name: 'Hounslow',
66+
srcDark: '/static/img/social-proof/hounslow.svg',
67+
srcLight: '/static/img/social-proof/hounslow-light.svg',
68+
url: 'https://data.hounslow.gov.uk',
69+
style: 'grayscale dark:invert-0 opacity-80',
70+
width: 200,
71+
},
72+
]
73+
74+
const { theme, setTheme } = useTheme()
75+
return (
76+
<div className="text-center max-w-full mx-auto py-24 px-4 sm:px-6 lg:px-8 w-full ">
77+
<h2 className="text-base font-semibold text-theme-orange uppercase tracking-wide opacity-75">
78+
Highlights of PortalJS & CKAN implementations
79+
</h2>
80+
<div className="max-w-8xl flex justify-center mt-5" tabIndex={0}>
81+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 justify-center items-center gap-x-4 gap-y-5 w-full mt-6">
82+
{logos.map((logo, index) => (
83+
<Link
84+
className="flex items-center justify-center w-full h-full max-h-24 p-2 opacity-75 hover:opacity-100 transition-all duration-300"
85+
key={logo.srcDark + index}
86+
title={logo.name}
87+
href={logo.url}
88+
>
89+
<Image
90+
className={`
91+
92+
h-auto ${logo.style}`}
93+
src={theme === 'light' ? logo.srcLight : logo.srcDark}
94+
alt={`${logo.name} Logo`}
95+
title={logo.name}
96+
height={100}
97+
width={logo.width}
98+
/>
99+
</Link>
100+
))}
101+
</div>
102+
</div>
103+
</div>
104+
)
105+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { Player } from '@lottiefiles/react-lottie-player'
2+
import { H2, H3 } from '../custom/header'
3+
import { useTheme } from 'next-themes'
4+
5+
export const WhyCKANAndPortalJS = () => {
6+
const { theme } = useTheme()
7+
const features = [
8+
{
9+
title: 'Modular architecture',
10+
description:
11+
'Decouples frontend from backend logic, enabling faster innovation without touching CKAN’s core.',
12+
icon: `/static/icons/${theme}/puzzle.json`,
13+
style: 'dark:-rotate-[4deg]',
14+
},
15+
{
16+
title: 'Rapid Development',
17+
description:
18+
'Offers copy-and-paste code snippets, so you can assemble pages and widgets in minutes.',
19+
icon: `/static/icons/${theme}/api.json`,
20+
style: 'dark:-rotate-[3deg]',
21+
},
22+
{
23+
title: 'Custom Branding',
24+
description:
25+
'Supports your branding with easy theming and custom components to match any style guide.',
26+
icon: `/static/icons/${theme}/data-catalog.json`,
27+
style: 'dark:-rotate-[2deg]',
28+
},
29+
{
30+
title: 'Optimized Performance',
31+
description:
32+
'Delivers performance out of the box with SSR-friendly React components for SEO and fast load times.',
33+
icon: `/static/icons/${theme}/cloud-network.json`,
34+
style: 'dark:-rotate-[4deg]',
35+
},
36+
]
37+
38+
return (
39+
<div className="py-24">
40+
<div className="">
41+
<H2 className="text-center">WHY CKAN & PortalJS?</H2>
42+
<H3 className="text-center">CKAN is the world’s leading open-source data management system, trusted by governments and enterprises to catalog and publish data. PortalJS complements CKAN by providing a lightweight, customizable frontend layer that:</H3>
43+
<div className="mt-16 grid grid-cols-1 gap-y-12 sm:grid-cols-2 sm:gap-x-12 lg:grid-cols-4 lg:gap-x-6">
44+
{features.map((feature) => (
45+
<div
46+
key={feature.title}
47+
className="relative flex flex-col rounded-xl dark:bg-slate-900 dark:hover:bg-slate-800 hover:bg-slate-100 transition-all duration-300 ring-1 ring-slate-200 dark:ring-slate-800 p-7 rounded-lg shadow-lg overflow-hidden"
48+
>
49+
<div className="flex-shrink-0 w-full flex items-start -ml-2">
50+
<Player
51+
autoplay
52+
loop
53+
src={feature.icon}
54+
className={`w-14 h-14 ${feature.style}`}
55+
/>
56+
</div>
57+
<div className="pt-4">
58+
<h3 className="text-lg font-medium ">{feature.title}</h3>
59+
<p className="mt-4 text-base ">
60+
{feature.description}
61+
</p>
62+
</div>
63+
</div>
64+
))}
65+
</div>
66+
</div>
67+
</div>
68+
)
69+
}

0 commit comments

Comments
 (0)