[monorepo][lg] - start of monorepo
This commit is contained in:
@@ -1,15 +0,0 @@
|
||||
type LinkProps = {
|
||||
url: string;
|
||||
format: any;
|
||||
};
|
||||
|
||||
const CustomLink: React.FC<LinkProps> = ({ url, format }: LinkProps) => (
|
||||
<a
|
||||
href={url}
|
||||
className="bg-white hover:bg-gray-200 border text-black font-semibold py-2 px-4 rounded"
|
||||
>
|
||||
{format}
|
||||
</a>
|
||||
);
|
||||
|
||||
export default CustomLink;
|
||||
@@ -1,17 +0,0 @@
|
||||
const ErrorMessage: React.FC<{ message: any }> = ({ message }) => {
|
||||
return (
|
||||
<aside>
|
||||
{message}
|
||||
<style jsx>{`
|
||||
aside {
|
||||
padding: 1.5em;
|
||||
font-size: 14px;
|
||||
color: white;
|
||||
background-color: red;
|
||||
}
|
||||
`}</style>
|
||||
</aside>
|
||||
);
|
||||
};
|
||||
|
||||
export default ErrorMessage;
|
||||
@@ -1,53 +0,0 @@
|
||||
interface TableProps {
|
||||
columns: Array<any>;
|
||||
data: Array<any>;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Table: React.FC<TableProps> = ({ columns, data, className }) => {
|
||||
return (
|
||||
<div className="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
||||
<div className="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
|
||||
<div className="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg">
|
||||
<table
|
||||
className={`min-w-full divide-y divide-gray-200 ${className}`}
|
||||
>
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
{columns.map(({ key, name }) => (
|
||||
<th
|
||||
key={key}
|
||||
scope="col"
|
||||
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
|
||||
>
|
||||
{name}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="bg-white divide-y divide-gray-200">
|
||||
{data.map((item) => (
|
||||
<tr key={item.id}>
|
||||
{columns.map(({ key, render }) => (
|
||||
<td
|
||||
key={key}
|
||||
className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900"
|
||||
>
|
||||
{(render &&
|
||||
typeof render === 'function' &&
|
||||
render(item)) ||
|
||||
item[key] ||
|
||||
''}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Table;
|
||||
@@ -1,5 +0,0 @@
|
||||
import Table from './Table';
|
||||
import ErrorMessage from './Error';
|
||||
import CustomLink from './CustomLink';
|
||||
|
||||
export { Table, ErrorMessage, CustomLink };
|
||||
@@ -1,83 +0,0 @@
|
||||
import { useQuery } from '@apollo/react-hooks';
|
||||
import * as timeago from 'timeago.js';
|
||||
import { ErrorMessage } from '../_shared';
|
||||
import { GET_DATASET_QUERY } from '../../graphql/queries';
|
||||
|
||||
const About: React.FC<{ variables: any }> = ({ variables }) => {
|
||||
const { loading, error, data } = useQuery(GET_DATASET_QUERY, {
|
||||
variables,
|
||||
// Setting this value to true will make the component rerender when
|
||||
// the "networkStatus" changes, so we are able to know if it is fetching
|
||||
// more data
|
||||
notifyOnNetworkStatusChange: true,
|
||||
});
|
||||
|
||||
if (error) return <ErrorMessage message="Error loading dataset." />;
|
||||
if (loading) return <div>Loading</div>;
|
||||
|
||||
const { result } = data.dataset;
|
||||
|
||||
const stats = [
|
||||
{ name: 'Files', stat: result.resources.length },
|
||||
{ name: 'Size', stat: result.size || 'N/A' },
|
||||
{
|
||||
name: 'Formats',
|
||||
stat: result.resources.map((item) => item.format).join(', '),
|
||||
},
|
||||
{
|
||||
name: 'Created',
|
||||
stat: result.created && timeago.format(result.created),
|
||||
},
|
||||
{
|
||||
name: 'Updated',
|
||||
stat: result.updated && timeago.format(result.updated),
|
||||
},
|
||||
{
|
||||
name: 'Licenses',
|
||||
stat: result.licenses?.length
|
||||
? result.licenses.map((item, index) => (
|
||||
<a
|
||||
className="text-yellow-600"
|
||||
href={item.path || '#'}
|
||||
title={item.title || ''}
|
||||
key={index}
|
||||
>
|
||||
{item.name}
|
||||
</a>
|
||||
))
|
||||
: 'N/A',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="pb-5 border-b border-gray-200">
|
||||
<h1 className="text-3xl leading-6 font-medium text-gray-900">
|
||||
{result.title || result.name}
|
||||
</h1>
|
||||
<p className="mt-2 max-w-4xl text-sm text-gray-500">
|
||||
{result.description || 'This dataset does not have a description.'}
|
||||
</p>
|
||||
</div>
|
||||
<div className="mb-5">
|
||||
<dl className="mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
|
||||
{stats.map((item) => (
|
||||
<div
|
||||
key={item.name}
|
||||
className="px-4 py-5 bg-white shadow rounded-lg overflow-hidden sm:p-6"
|
||||
>
|
||||
<dt className="text-sm font-medium text-gray-500 truncate">
|
||||
{item.name}
|
||||
</dt>
|
||||
<dd className="mt-1 text-3xl font-semibold text-gray-900">
|
||||
{item.stat}
|
||||
</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default About;
|
||||
@@ -1,23 +0,0 @@
|
||||
import { useQuery } from '@apollo/react-hooks';
|
||||
import { ErrorMessage } from '../_shared';
|
||||
import { GET_DATASET_QUERY } from '../../graphql/queries';
|
||||
import { Org } from 'portal';
|
||||
|
||||
const OrgInfo: React.FC<{ variables: any }> = ({ variables }) => {
|
||||
const { loading, error, data } = useQuery(GET_DATASET_QUERY, {
|
||||
variables,
|
||||
// Setting this value to true will make the component rerender when
|
||||
// the "networkStatus" changes, so we are able to know if it is fetching
|
||||
// more data
|
||||
notifyOnNetworkStatusChange: true,
|
||||
});
|
||||
|
||||
if (error) return <ErrorMessage message="Error loading dataset." />;
|
||||
if (loading) return <div>Loading</div>;
|
||||
|
||||
const { organization } = data.dataset.result;
|
||||
|
||||
return <Org organization={organization} />;
|
||||
};
|
||||
|
||||
export default OrgInfo;
|
||||
@@ -1,76 +0,0 @@
|
||||
/* eslint-disable jsx-a11y/anchor-is-valid */
|
||||
/* eslint-disable react/display-name */
|
||||
import Link from 'next/link';
|
||||
import { useQuery } from '@apollo/react-hooks';
|
||||
import * as timeago from 'timeago.js';
|
||||
import { Table, ErrorMessage } from '../_shared';
|
||||
import { GET_DATASET_QUERY } from '../../graphql/queries';
|
||||
|
||||
const columns = [
|
||||
{
|
||||
name: 'File',
|
||||
key: 'file',
|
||||
render: ({ name: resName, title, parentName }) => (
|
||||
<Link href={`${parentName}/r/${resName}`}>
|
||||
<a className="underline">{title || resName}</a>
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
name: 'Format',
|
||||
key: 'format',
|
||||
},
|
||||
{
|
||||
name: 'Created',
|
||||
key: 'created',
|
||||
render: ({ created }) => timeago.format(created),
|
||||
},
|
||||
{
|
||||
name: 'Updated',
|
||||
key: 'updated',
|
||||
render: ({ updated }) => timeago.format(updated),
|
||||
},
|
||||
{
|
||||
name: 'Link',
|
||||
key: 'link',
|
||||
render: ({ name: resName, parentName }) => (
|
||||
<Link href={`${parentName}/r/${resName}`}>
|
||||
<a className="underline">Preview</a>
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const Resources: React.FC<{ variables: any }> = ({ variables }) => {
|
||||
const { loading, error, data } = useQuery(GET_DATASET_QUERY, {
|
||||
variables,
|
||||
// Setting this value to true will make the component rerender when
|
||||
// the "networkStatus" changes, so we are able to know if it is fetching
|
||||
// more data
|
||||
notifyOnNetworkStatusChange: true,
|
||||
});
|
||||
|
||||
if (error) return <ErrorMessage message="Error loading dataset." />;
|
||||
if (loading) return <div>Loading</div>;
|
||||
|
||||
const { result } = data.dataset;
|
||||
|
||||
return (
|
||||
<div className="mt-12">
|
||||
<div className="pb-5 border-b border-gray-200">
|
||||
<h1 className="text-2xl leading-6 font-medium text-gray-900">
|
||||
Data files
|
||||
</h1>
|
||||
</div>
|
||||
<Table
|
||||
columns={columns}
|
||||
data={result.resources.map((resource) => ({
|
||||
...resource,
|
||||
parentName: result.name,
|
||||
}))}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Resources;
|
||||
@@ -1,61 +0,0 @@
|
||||
const Footer: React.FC = () => {
|
||||
const navigation = {
|
||||
main: [
|
||||
{ name: 'Blog', href: '/blog' },
|
||||
{ name: 'Search', href: '/search' },
|
||||
{ name: 'Docs', href: '/docs' },
|
||||
],
|
||||
social: [
|
||||
{
|
||||
name: 'GitHub',
|
||||
href: 'https://github.com/datopian/portal.js',
|
||||
// eslint-disable-next-line
|
||||
icon: (props) => (
|
||||
<svg fill="currentColor" viewBox="0 0 24 24" {...props}>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
return (
|
||||
<footer className="bg-white">
|
||||
<div className="max-w-7xl mx-auto py-12 px-4 overflow-hidden sm:px-6 lg:px-8">
|
||||
<nav
|
||||
className="-mx-5 -my-2 flex flex-wrap justify-center"
|
||||
aria-label="Footer"
|
||||
>
|
||||
{navigation.main.map((item) => (
|
||||
<div key={item.name} className="px-5 py-2">
|
||||
<a
|
||||
href={item.href}
|
||||
className="text-base text-gray-500 hover:text-gray-900"
|
||||
>
|
||||
{item.name}
|
||||
</a>
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
<div className="mt-8 flex justify-center space-x-6">
|
||||
{navigation.social.map((item) => (
|
||||
<a
|
||||
key={item.name}
|
||||
href={item.href}
|
||||
className="text-gray-400 hover:text-gray-500"
|
||||
>
|
||||
<span className="sr-only">{item.name}</span>
|
||||
<item.icon className="h-6 w-6" aria-hidden="true" />
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
@@ -1,7 +0,0 @@
|
||||
import Template from './HeroTemplate';
|
||||
|
||||
const Hero: React.FC = () => {
|
||||
return <Template />;
|
||||
};
|
||||
|
||||
export default Hero;
|
||||
@@ -1,130 +0,0 @@
|
||||
import useTranslation from 'next-translate/useTranslation';
|
||||
import SearchForm from '../search/Form';
|
||||
|
||||
export default function Example() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="relative bg-white overflow-hidden">
|
||||
<div
|
||||
className="hidden lg:block lg:absolute lg:inset-0"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<svg
|
||||
className="absolute top-0 left-1/2 transform translate-x-64 -translate-y-8"
|
||||
width={640}
|
||||
height={784}
|
||||
fill="none"
|
||||
viewBox="0 0 640 784"
|
||||
>
|
||||
<defs>
|
||||
<pattern
|
||||
id="9ebea6f4-a1f5-4d96-8c4e-4c2abf658047"
|
||||
x={118}
|
||||
y={0}
|
||||
width={20}
|
||||
height={20}
|
||||
patternUnits="userSpaceOnUse"
|
||||
>
|
||||
<rect
|
||||
x={0}
|
||||
y={0}
|
||||
width={4}
|
||||
height={4}
|
||||
className="text-gray-200"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect
|
||||
y={72}
|
||||
width={640}
|
||||
height={640}
|
||||
className="text-gray-50"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<rect
|
||||
x={118}
|
||||
width={404}
|
||||
height={784}
|
||||
fill="url(#9ebea6f4-a1f5-4d96-8c4e-4c2abf658047)"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div className="relative pt-6 pb-16 sm:pb-24 lg:pb-32">
|
||||
<main className="mt-16 mx-auto max-w-7xl px-4 sm:mt-24 sm:px-6 lg:mt-32">
|
||||
<div className="lg:grid lg:grid-cols-12 lg:gap-8">
|
||||
<div className="sm:text-center md:max-w-2xl md:mx-auto lg:col-span-6 lg:text-left">
|
||||
<h1>
|
||||
<span className="block text-sm font-semibold uppercase tracking-wide text-gray-500 sm:text-base lg:text-sm xl:text-base">
|
||||
Quality Data ready to Integrate
|
||||
</span>
|
||||
<span className="mt-1 block text-4xl tracking-tight font-extrabold sm:text-5xl xl:text-6xl">
|
||||
<span className="block text-gray-900">Find and Share</span>
|
||||
<span className="block text-indigo-600">Quality Data</span>
|
||||
</span>
|
||||
</h1>
|
||||
<p className="mt-3 text-base text-gray-500 sm:mt-5 sm:text-xl lg:text-lg xl:text-xl">
|
||||
{t(`common:description`)}
|
||||
</p>
|
||||
<div className="mt-8 sm:max-w-lg sm:mx-auto sm:text-center lg:text-left lg:mx-0">
|
||||
<SearchForm />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-12 relative sm:max-w-lg sm:mx-auto lg:mt-0 lg:max-w-none lg:mx-0 lg:col-span-6 lg:flex lg:items-center">
|
||||
<svg
|
||||
className="absolute top-0 left-1/2 transform -translate-x-1/2 -translate-y-8 scale-75 origin-top sm:scale-100 lg:hidden"
|
||||
width={640}
|
||||
height={784}
|
||||
fill="none"
|
||||
viewBox="0 0 640 784"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<defs>
|
||||
<pattern
|
||||
id="4f4f415c-a0e9-44c2-9601-6ded5a34a13e"
|
||||
x={118}
|
||||
y={0}
|
||||
width={20}
|
||||
height={20}
|
||||
patternUnits="userSpaceOnUse"
|
||||
>
|
||||
<rect
|
||||
x={0}
|
||||
y={0}
|
||||
width={4}
|
||||
height={4}
|
||||
className="text-gray-200"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect
|
||||
y={72}
|
||||
width={640}
|
||||
height={640}
|
||||
className="text-gray-50"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<rect
|
||||
x={118}
|
||||
width={404}
|
||||
height={784}
|
||||
fill="url(#4f4f415c-a0e9-44c2-9601-6ded5a34a13e)"
|
||||
/>
|
||||
</svg>
|
||||
<div className="relative mx-auto w-full rounded-lg shadow-lg lg:max-w-md">
|
||||
<img
|
||||
className="w-full"
|
||||
src="/images/banner.svg"
|
||||
alt="banner_img"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import Template from './NavTemplate';
|
||||
|
||||
const NavBar: React.FC = () => {
|
||||
const navMenu = [
|
||||
{ title: 'Blog', path: '/blog' },
|
||||
{ title: 'Search', path: '/search' },
|
||||
{ title: 'Docs', path: 'http://tech.datopian.com/frontend/' },
|
||||
{ title: 'GitHub', path: 'https://github.com/datopian/portal.js' },
|
||||
];
|
||||
|
||||
return <Template menu={navMenu} logo={'/images/logo.svg'} />;
|
||||
};
|
||||
|
||||
export default NavBar;
|
||||
@@ -1,118 +0,0 @@
|
||||
import { useState } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { Disclosure } from '@headlessui/react';
|
||||
import { SearchIcon } from '@heroicons/react/solid';
|
||||
import { MenuIcon, XIcon } from '@heroicons/react/outline';
|
||||
|
||||
const NavBar: React.FC<{ menu: any; logo: string }> = ({ menu, logo }) => {
|
||||
const router = useRouter();
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
router.push({
|
||||
pathname: '/search',
|
||||
query: { q: searchQuery },
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Disclosure as="nav" className="bg-white shadow">
|
||||
{({ open }) => (
|
||||
<>
|
||||
<div className="max-w-7xl mx-auto px-2 sm:px-4 lg:px-8">
|
||||
<div className="flex justify-between h-16">
|
||||
<div className="flex px-2 lg:px-0">
|
||||
<div className="flex-shrink-0 flex items-center">
|
||||
<a href="/">
|
||||
<img
|
||||
className="block lg:hidden h-8 w-auto"
|
||||
src={logo}
|
||||
alt="Portal.js"
|
||||
/>
|
||||
<img
|
||||
className="hidden lg:block h-8 w-auto"
|
||||
src={logo}
|
||||
alt="Portal.js"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<div className="hidden lg:ml-6 lg:flex lg:space-x-8">
|
||||
{/* Current: "border-indigo-500 text-gray-900", Default: "border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700" */}
|
||||
{menu.map((item, index) => (
|
||||
<a
|
||||
key={'menu-link' + index}
|
||||
href={item.path}
|
||||
className="border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium"
|
||||
>
|
||||
{item.title}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 flex items-center justify-center px-2 lg:ml-6 lg:justify-end">
|
||||
<div className="max-w-lg w-full lg:max-w-xs">
|
||||
<label htmlFor="search" className="sr-only">
|
||||
Search
|
||||
</label>
|
||||
<div className="relative">
|
||||
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<SearchIcon
|
||||
className="h-5 w-5 text-gray-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
<form
|
||||
onSubmit={(e) => handleSubmit(e)}
|
||||
className="items-center"
|
||||
>
|
||||
<input
|
||||
id="search"
|
||||
type="search"
|
||||
name="search"
|
||||
onChange={(e) => {
|
||||
setSearchQuery(e.target.value);
|
||||
}}
|
||||
placeholder="Search"
|
||||
aria-label="Search"
|
||||
className="block w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md leading-5 bg-white placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center lg:hidden">
|
||||
{/* Mobile menu button */}
|
||||
<Disclosure.Button className="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500">
|
||||
<span className="sr-only">Open main menu</span>
|
||||
{open ? (
|
||||
<XIcon className="block h-6 w-6" aria-hidden="true" />
|
||||
) : (
|
||||
<MenuIcon className="block h-6 w-6" aria-hidden="true" />
|
||||
)}
|
||||
</Disclosure.Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Disclosure.Panel className="lg:hidden">
|
||||
<div className="pt-2 pb-3 space-y-1">
|
||||
{/* Current: "bg-indigo-50 border-indigo-500 text-indigo-700", Default: "border-transparent text-gray-600 hover:bg-gray-50 hover:border-gray-300 hover:text-gray-800" */}
|
||||
{menu.map((item, index) => (
|
||||
<a
|
||||
key={'mobile-menu-link' + index}
|
||||
href={item.path}
|
||||
className="border-transparent text-gray-600 hover:bg-gray-50 hover:border-gray-300 hover:text-gray-800 block pl-3 pr-4 py-2 border-l-4 text-base font-medium"
|
||||
>
|
||||
{item.title}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</Disclosure.Panel>
|
||||
</>
|
||||
)}
|
||||
</Disclosure>
|
||||
);
|
||||
};
|
||||
|
||||
export default NavBar;
|
||||
@@ -1,96 +0,0 @@
|
||||
/* eslint-disable jsx-a11y/anchor-is-valid */
|
||||
import { useQuery } from '@apollo/react-hooks';
|
||||
import {
|
||||
CollectionIcon,
|
||||
PresentationChartBarIcon,
|
||||
} from '@heroicons/react/solid';
|
||||
import { ErrorMessage } from '../_shared';
|
||||
import { SEARCH_QUERY } from '../../graphql/queries';
|
||||
|
||||
const RecentDataset: React.FC = () => {
|
||||
const { loading, error, data } = useQuery(SEARCH_QUERY, {
|
||||
variables: {
|
||||
sort: 'metadata_created desc',
|
||||
rows: 3,
|
||||
},
|
||||
// Setting this value to true will make the component rerender when
|
||||
// the "networkStatus" changes, so we are able to know if it is fetching
|
||||
// more data
|
||||
notifyOnNetworkStatusChange: true,
|
||||
});
|
||||
|
||||
if (error) return <ErrorMessage message="Error loading search results." />;
|
||||
if (loading) return <div>Loading</div>;
|
||||
|
||||
const { result } = data.search;
|
||||
|
||||
return (
|
||||
<section className="my-10 p-4">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900">
|
||||
Recent Datasets
|
||||
</h3>
|
||||
<ul className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{result.results.map((dataset) => (
|
||||
<li
|
||||
key={dataset.id}
|
||||
className="col-span-1 bg-white rounded-lg shadow divide-y divide-gray-200"
|
||||
>
|
||||
<div className="w-full flex items-center justify-between p-6 space-x-6">
|
||||
<div className="flex-1 truncate">
|
||||
<div className="flex items-center space-x-3">
|
||||
<h3 className="text-gray-900 text-sm font-medium truncate">
|
||||
{dataset.title || dataset.name}
|
||||
</h3>
|
||||
<span className="flex-shrink-0 inline-block px-2 py-0.5 text-green-800 text-xs font-medium bg-green-100 rounded-full">
|
||||
dataset
|
||||
</span>
|
||||
</div>
|
||||
<p className="mt-1 text-gray-500 text-sm truncate">
|
||||
{dataset.organization.title || dataset.organization.name}
|
||||
</p>
|
||||
</div>
|
||||
<img
|
||||
className="w-10 h-10 bg-gray-300 rounded-full flex-shrink-0"
|
||||
src={
|
||||
dataset.organization.image ||
|
||||
'https://datahub.io/static/img/datahub-cube-edited.svg'
|
||||
}
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div className="-mt-px flex divide-x divide-gray-200">
|
||||
<div className="w-0 flex-1 flex">
|
||||
<a
|
||||
href={`/@${dataset.organization.name}`}
|
||||
className="relative -mr-px w-0 flex-1 inline-flex items-center justify-center py-4 text-sm text-gray-700 font-medium border border-transparent rounded-bl-lg hover:text-gray-500"
|
||||
>
|
||||
<CollectionIcon
|
||||
className="w-5 h-5 text-gray-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span className="ml-3">Organization</span>
|
||||
</a>
|
||||
</div>
|
||||
<div className="-ml-px w-0 flex-1 flex">
|
||||
<a
|
||||
href={`/@${dataset.organization.name}/${dataset.name}`}
|
||||
className="relative w-0 flex-1 inline-flex items-center justify-center py-4 text-sm text-gray-700 font-medium border border-transparent rounded-br-lg hover:text-gray-500"
|
||||
>
|
||||
<PresentationChartBarIcon
|
||||
className="w-5 h-5 text-gray-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span className="ml-3">Preview</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default RecentDataset;
|
||||
@@ -1,54 +0,0 @@
|
||||
/* eslint-disable jsx-a11y/anchor-is-valid */
|
||||
import { useQuery } from '@apollo/react-hooks';
|
||||
import { ErrorMessage } from '../_shared';
|
||||
import { GET_STATS_QUERY } from '../../graphql/queries';
|
||||
|
||||
const Stats: React.FC = () => {
|
||||
const { loading, error, data } = useQuery(GET_STATS_QUERY, {
|
||||
variables: {},
|
||||
// Setting this value to true will make the component rerender when
|
||||
// the "networkStatus" changes, so we are able to know if it is fetching
|
||||
// more data
|
||||
notifyOnNetworkStatusChange: true,
|
||||
});
|
||||
|
||||
if (error) return <ErrorMessage message="Error loading search results." />;
|
||||
if (loading) return <div>Loading</div>;
|
||||
|
||||
const stats = [
|
||||
{ name: 'Datasets', stat: data.datasets.result.count },
|
||||
{
|
||||
name: 'Organizations',
|
||||
stat: data.orgs.result ? data.orgs.result.length : 0,
|
||||
},
|
||||
{
|
||||
name: 'Groups',
|
||||
stat: data.groups.result ? data.groups.result.length : 0,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="p-4">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900">
|
||||
DataHub Stats
|
||||
</h3>
|
||||
<dl className="mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
|
||||
{stats.map((item) => (
|
||||
<div
|
||||
key={item.name}
|
||||
className="px-4 py-5 bg-white shadow rounded-lg overflow-hidden sm:p-6"
|
||||
>
|
||||
<dt className="text-sm font-medium text-gray-500 truncate">
|
||||
{item.name}
|
||||
</dt>
|
||||
<dd className="mt-1 text-3xl font-semibold text-gray-900">
|
||||
{item.stat}
|
||||
</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Stats;
|
||||
@@ -1,154 +0,0 @@
|
||||
import { useQuery } from '@apollo/react-hooks';
|
||||
import * as timeago from 'timeago.js';
|
||||
import { ErrorMessage } from '../_shared';
|
||||
import { GET_ORG_QUERY } from '../../graphql/queries';
|
||||
|
||||
const About: React.FC<{ variables: any }> = ({ variables }) => {
|
||||
const { loading, error, data } = useQuery(GET_ORG_QUERY, {
|
||||
variables,
|
||||
// Setting this value to true will make the component rerender when
|
||||
// the "networkStatus" changes, so we are able to know if it is fetching
|
||||
// more data
|
||||
notifyOnNetworkStatusChange: true,
|
||||
});
|
||||
|
||||
if (error) return <ErrorMessage message="Error loading dataset." />;
|
||||
if (loading) return <div>Loading</div>;
|
||||
|
||||
const { result } = data.org;
|
||||
|
||||
return (
|
||||
<div className="relative bg-white py-16 sm:py-4">
|
||||
<div className="lg:mx-auto lg:max-w-7xl lg:px-8 lg:grid lg:grid-cols-2 lg:gap-24 lg:items-start">
|
||||
<div className="relative sm:py-16 lg:py-0">
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="hidden sm:block lg:absolute lg:inset-y-0 lg:right-0 lg:w-screen"
|
||||
>
|
||||
<div className="absolute inset-y-0 right-1/2 w-full bg-gray-50 rounded-r-3xl lg:right-72" />
|
||||
<svg
|
||||
className="absolute top-8 left-1/2 -ml-3 lg:-right-8 lg:left-auto lg:top-12"
|
||||
width={404}
|
||||
height={392}
|
||||
fill="none"
|
||||
viewBox="0 0 404 392"
|
||||
>
|
||||
<defs>
|
||||
<pattern
|
||||
id="02f20b47-fd69-4224-a62a-4c9de5c763f7"
|
||||
x={0}
|
||||
y={0}
|
||||
width={20}
|
||||
height={20}
|
||||
patternUnits="userSpaceOnUse"
|
||||
>
|
||||
<rect
|
||||
x={0}
|
||||
y={0}
|
||||
width={4}
|
||||
height={4}
|
||||
className="text-gray-200"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect
|
||||
width={404}
|
||||
height={392}
|
||||
fill="url(#02f20b47-fd69-4224-a62a-4c9de5c763f7)"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="relative mx-auto max-w-md px-4 sm:max-w-3xl sm:px-6 lg:px-0 lg:max-w-none lg:py-20">
|
||||
{/* Testimonial card*/}
|
||||
<div className="relative pt-64 pb-10 rounded-2xl shadow-xl overflow-hidden">
|
||||
<img
|
||||
className="absolute inset-0 h-full w-full object-cover"
|
||||
src={
|
||||
result.image ||
|
||||
'https://datahub.io/static/img/datahub-cube-edited.svg'
|
||||
}
|
||||
alt={result.title || result.name}
|
||||
/>
|
||||
<div className="absolute inset-0 bg-indigo-500 mix-blend-multiply" />
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-indigo-600 via-indigo-600 opacity-90" />
|
||||
<div className="relative px-8">
|
||||
<blockquote className="mt-8">
|
||||
<div className="relative text-lg font-medium text-white md:flex-grow">
|
||||
<p className="relative">
|
||||
{result.description ||
|
||||
"This organization doesn't have a description."}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<footer className="mt-4">
|
||||
<p className="text-base font-semibold text-indigo-200">
|
||||
{result.title}
|
||||
</p>
|
||||
</footer>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative mx-auto max-w-md px-4 sm:max-w-3xl sm:px-6 lg:px-0">
|
||||
{/* Content area */}
|
||||
<div className="pt-12 sm:pt-16 lg:pt-20">
|
||||
<h1 className="text-3xl text-gray-900 font-extrabold tracking-tight sm:text-4xl">
|
||||
{result.title || result.name}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
{/* Stats section */}
|
||||
<div className="mt-10">
|
||||
<dl className="grid grid-cols-2 gap-x-4 gap-y-8">
|
||||
<div className="border-t-2 border-gray-100 pt-6">
|
||||
<dt className="text-base font-medium text-gray-500">
|
||||
Datasets
|
||||
</dt>
|
||||
<dd className="text-3xl font-extrabold tracking-tight text-gray-900">
|
||||
{result.total}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="border-t-2 border-gray-100 pt-6">
|
||||
<dt className="text-base font-medium text-gray-500">Users</dt>
|
||||
<dd className="text-3xl font-extrabold tracking-tight text-gray-900">
|
||||
{result.users && result.users.length}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="border-t-2 border-gray-100 pt-6">
|
||||
<dt className="text-base font-medium text-gray-500">
|
||||
Followers
|
||||
</dt>
|
||||
<dd className="text-3xl font-extrabold tracking-tight text-gray-900">
|
||||
{result.followers}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="border-t-2 border-gray-100 pt-6">
|
||||
<dt className="text-base font-medium text-gray-500">
|
||||
Created
|
||||
</dt>
|
||||
<dd className="text-3xl font-extrabold tracking-tight text-gray-900">
|
||||
{timeago.format(result.created)}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
<div className="mt-10">
|
||||
<a
|
||||
href={`/search?fq=organization:${result.name}`}
|
||||
className="text-base font-medium text-indigo-600"
|
||||
>
|
||||
{' '}
|
||||
Datasets by {result.title || result.name}{' '}
|
||||
<span aria-hidden="true">→</span>{' '}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default About;
|
||||
@@ -1,78 +0,0 @@
|
||||
/* eslint-disable react/display-name */
|
||||
import { useQuery } from '@apollo/react-hooks';
|
||||
import * as timeago from 'timeago.js';
|
||||
import { ErrorMessage } from '../_shared';
|
||||
import { GET_DATASET_QUERY } from '../../graphql/queries';
|
||||
|
||||
const About: React.FC<{ variables: any }> = ({ variables }) => {
|
||||
const { loading, error, data } = useQuery(GET_DATASET_QUERY, {
|
||||
variables,
|
||||
// Setting this value to true will make the component rerender when
|
||||
// the "networkStatus" changes, so we are able to know if it is fetching
|
||||
// more data
|
||||
notifyOnNetworkStatusChange: true,
|
||||
});
|
||||
|
||||
if (error) return <ErrorMessage message="Error loading dataset." />;
|
||||
if (loading) return <div>Loading</div>;
|
||||
|
||||
const { result } = data.dataset;
|
||||
const resource = result.resources.find(
|
||||
(item) => item.name === variables.resource
|
||||
);
|
||||
|
||||
const stats = [
|
||||
{ name: 'File', stat: resource.title || resource.name },
|
||||
{ name: 'Description', stat: resource.description || 'N/A' },
|
||||
{ name: 'Size', stat: resource.size || 'N/A' },
|
||||
{
|
||||
name: 'Created',
|
||||
stat: resource.created && timeago.format(resource.created),
|
||||
},
|
||||
{
|
||||
name: 'Updated',
|
||||
stat: resource.updated && timeago.format(resource.updated),
|
||||
},
|
||||
{ name: 'Download', stat: resource.path, link: true },
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="pb-5 border-b border-gray-200">
|
||||
<h1 className="text-3xl leading-6 font-medium text-gray-900">
|
||||
{result.title || result.name}
|
||||
</h1>
|
||||
</div>
|
||||
<div className="mb-5">
|
||||
<dl className="mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
|
||||
{stats.map((item) => (
|
||||
<div
|
||||
key={item.name}
|
||||
className="px-4 py-5 bg-white shadow rounded-lg overflow-hidden sm:p-6"
|
||||
>
|
||||
<dt className="text-sm font-medium text-gray-500 truncate">
|
||||
{item.name}
|
||||
</dt>
|
||||
<dd className="mt-1 text-3xl font-semibold text-gray-900">
|
||||
{item.link ? (
|
||||
<a
|
||||
href={item.stat}
|
||||
className="underline"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
{resource.format || 'Click'}
|
||||
</a>
|
||||
) : (
|
||||
item.stat
|
||||
)}
|
||||
</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default About;
|
||||
@@ -1,32 +0,0 @@
|
||||
import { useQuery } from '@apollo/react-hooks';
|
||||
import { PlotlyChart, Table } from 'portal';
|
||||
import { ErrorMessage } from '../_shared';
|
||||
import { GET_DATASTORE_DATA } from '../../graphql/queries';
|
||||
|
||||
const Preview: React.FC<{ view: any }> = ({ view }) => {
|
||||
const variables = {
|
||||
resource_id: view.resources,
|
||||
};
|
||||
const { loading, error, data } = useQuery(GET_DATASTORE_DATA, {
|
||||
variables,
|
||||
// Setting this value to true will make the component rerender when
|
||||
// the "networkStatus" changes, so we are able to know if it is fetching
|
||||
// more data
|
||||
notifyOnNetworkStatusChange: true,
|
||||
});
|
||||
|
||||
if (error) return <ErrorMessage message="Error loading dataset." />;
|
||||
if (loading) return <div>Loading</div>;
|
||||
|
||||
const { result } = data.datastore;
|
||||
|
||||
// Assuming for now it is always a table view
|
||||
const columns = result.fields.map((field) => ({
|
||||
field: field.id,
|
||||
headerName: field.id,
|
||||
}));
|
||||
|
||||
return <Table columns={columns} data={result.records} />;
|
||||
};
|
||||
|
||||
export default Preview;
|
||||
@@ -1,24 +0,0 @@
|
||||
import { useQuery } from '@apollo/react-hooks';
|
||||
import Preview from './Preview';
|
||||
import { ErrorMessage } from '../_shared';
|
||||
import { GET_RESOURCE_VIEWS } from '../../graphql/queries';
|
||||
|
||||
const View: React.FC<{ variables: any }> = ({ variables }) => {
|
||||
const { loading, error, data } = useQuery(GET_RESOURCE_VIEWS, {
|
||||
variables,
|
||||
// Setting this value to true will make the component rerender when
|
||||
// the "networkStatus" changes, so we are able to know if it is fetching
|
||||
// more data
|
||||
notifyOnNetworkStatusChange: true,
|
||||
});
|
||||
|
||||
if (error) return <ErrorMessage message="Error loading dataset." />;
|
||||
if (loading) return <div>Loading</div>;
|
||||
|
||||
const { result } = data.views;
|
||||
const previews = result.map((view) => <Preview view={view} key={view.id} />);
|
||||
|
||||
return <>{previews}</>;
|
||||
};
|
||||
|
||||
export default View;
|
||||
@@ -1,42 +0,0 @@
|
||||
import { useState } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
const SearchForm: React.FC = () => {
|
||||
const router = useRouter();
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
router.push({
|
||||
pathname: '/search',
|
||||
query: { q: searchQuery },
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={(e) => handleSubmit(e)} className="items-center">
|
||||
<input
|
||||
id="search2"
|
||||
type="search"
|
||||
name="search"
|
||||
onChange={(e) => {
|
||||
setSearchQuery(e.target.value);
|
||||
}}
|
||||
placeholder="GDP data..."
|
||||
aria-label="Search"
|
||||
className="inline-block w-1/2 pr-3 py-2 border border-gray-300 rounded-md leading-5 bg-white placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
|
||||
/>
|
||||
<button
|
||||
onClick={() => handleSubmit(false)}
|
||||
type="button"
|
||||
className="inline-block text-sm px-4 py-3 mx-3 leading-none border rounded text-white bg-black border-black lg:mt-0"
|
||||
>
|
||||
Search
|
||||
</button>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchForm;
|
||||
@@ -1,72 +0,0 @@
|
||||
import { useQuery } from '@apollo/react-hooks';
|
||||
import Link from 'next/link';
|
||||
import * as timeago from 'timeago.js';
|
||||
import { ErrorMessage } from '../_shared';
|
||||
import { SEARCH_QUERY } from '../../graphql/queries';
|
||||
|
||||
const List: React.FC<{ variables: any }> = ({ variables }) => {
|
||||
const { loading, error, data } = useQuery(SEARCH_QUERY, {
|
||||
variables,
|
||||
// Setting this value to true will make the component rerender when
|
||||
// the "networkStatus" changes, so we are able to know if it is fetching
|
||||
// more data
|
||||
notifyOnNetworkStatusChange: true,
|
||||
});
|
||||
|
||||
if (error) return <ErrorMessage message="Error loading search results." />;
|
||||
if (loading) return <div>Loading</div>;
|
||||
const { result } = data.search;
|
||||
return (
|
||||
<ul className="divide-y divide-gray-200">
|
||||
{result.results.map((dataset, index) => (
|
||||
<li
|
||||
key={index}
|
||||
className="relative bg-white py-5 px-4 hover:bg-gray-50 focus-within:ring-2 focus-within:ring-inset focus-within:ring-indigo-600"
|
||||
>
|
||||
<div className="flex justify-between space-x-3">
|
||||
<div className="min-w-0 flex-1">
|
||||
<Link
|
||||
href={`/@${
|
||||
dataset.organization ? dataset.organization.name : 'dataset'
|
||||
}/${dataset.name}`}
|
||||
>
|
||||
{/*
|
||||
Linting fails because href is required for anchor tags and it
|
||||
ignores NextJS's Link API. Please, see this issue for details:
|
||||
https://github.com/vercel/next.js/issues/5533
|
||||
*/}
|
||||
{/* eslint-disable-next-line */}
|
||||
<a className="block focus:outline-none">
|
||||
<span className="absolute inset-0" aria-hidden="true" />
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
{dataset.title}
|
||||
</p>
|
||||
<p className="text-sm text-gray-500 truncate">
|
||||
{dataset.organization
|
||||
? dataset.organization.title
|
||||
: 'dataset'}{' '}
|
||||
/ {dataset.name}
|
||||
</p>
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
<time
|
||||
dateTime={dataset.metadata_modified}
|
||||
className="flex-shrink-0 whitespace-nowrap text-sm text-gray-500"
|
||||
>
|
||||
Updated {timeago.format(dataset.metadata_modified)}
|
||||
</time>
|
||||
</div>
|
||||
<div className="mt-1">
|
||||
<p className="line-clamp-2 text-sm text-gray-600">
|
||||
{dataset.description ||
|
||||
"This dataset doesn't have a description."}
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
|
||||
export default List;
|
||||
@@ -1,23 +0,0 @@
|
||||
import { useQuery } from '@apollo/react-hooks';
|
||||
import { ErrorMessage } from '../_shared';
|
||||
import { GET_TOTAL_COUNT_QUERY } from '../../graphql/queries';
|
||||
import { ItemTotal } from 'portal';
|
||||
|
||||
const Total: React.FC<{ variables: any }> = ({ variables }) => {
|
||||
const { loading, error, data } = useQuery(GET_TOTAL_COUNT_QUERY, {
|
||||
variables,
|
||||
// Setting this value to true will make the component rerender when
|
||||
// the "networkStatus" changes, so we are able to know if it is fetching
|
||||
// more data
|
||||
notifyOnNetworkStatusChange: true,
|
||||
});
|
||||
|
||||
if (error) return <ErrorMessage message="Error loading search results." />;
|
||||
if (loading) return <div>Loading</div>;
|
||||
|
||||
const { result } = data.search;
|
||||
|
||||
return <ItemTotal count={result.count} />;
|
||||
};
|
||||
|
||||
export default Total;
|
||||
@@ -1,39 +0,0 @@
|
||||
import parse from 'html-react-parser';
|
||||
import { useQuery } from '@apollo/react-hooks';
|
||||
import { ErrorMessage } from '../_shared';
|
||||
import { GET_POSTS_QUERY } from '../../graphql/queries';
|
||||
|
||||
const List: React.FC = () => {
|
||||
const { loading, error, data } = useQuery(GET_POSTS_QUERY, {
|
||||
// Setting this value to true will make the component rerender when
|
||||
// the "networkStatus" changes, so we are able to know if it is fetching
|
||||
// more data
|
||||
notifyOnNetworkStatusChange: true,
|
||||
});
|
||||
|
||||
if (error) return <ErrorMessage message="Error loading search results." />;
|
||||
if (loading) return <div>Loading</div>;
|
||||
|
||||
const { posts, found } = data.posts;
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1 className="text-3xl font-semibold text-primary my-6 inline-block">
|
||||
{found} posts found
|
||||
</h1>
|
||||
{posts.map((post, index) => (
|
||||
<div key={index}>
|
||||
<a
|
||||
href={`/blog/${post.slug}`}
|
||||
className="text-2xl font-semibold text-primary my-6 inline-block"
|
||||
>
|
||||
{parse(post.title)}
|
||||
</a>
|
||||
<p>{parse(post.excerpt)}</p>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default List;
|
||||
@@ -1,32 +0,0 @@
|
||||
import parse from 'html-react-parser';
|
||||
import { useQuery } from '@apollo/react-hooks';
|
||||
import { ErrorMessage } from '../_shared';
|
||||
import { GET_PAGE_QUERY } from '../../graphql/queries';
|
||||
|
||||
const Page: React.FC<{ variables: any }> = ({ variables }) => {
|
||||
const { loading, error, data } = useQuery(GET_PAGE_QUERY, {
|
||||
variables,
|
||||
// Setting this value to true will make the component rerender when
|
||||
// the "networkStatus" changes, so we are able to know if it is fetching
|
||||
// more data
|
||||
notifyOnNetworkStatusChange: true,
|
||||
});
|
||||
|
||||
if (error) return <ErrorMessage message="Error loading search results." />;
|
||||
if (loading) return <div>Loading</div>;
|
||||
|
||||
const { title, content, modified, featured_image } = data.page;
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1 className="text-3xl font-semibold text-primary my-6 inline-block">
|
||||
{title}
|
||||
</h1>
|
||||
<p className="mb-6">Edited: {modified}</p>
|
||||
<img src={featured_image} className="mb-6" alt="featured_img" />
|
||||
<div>{parse(content)}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
@@ -1,32 +0,0 @@
|
||||
import parse from 'html-react-parser';
|
||||
import { useQuery } from '@apollo/react-hooks';
|
||||
import { ErrorMessage } from '../_shared';
|
||||
import { GET_PAGE_QUERY } from '../../graphql/queries';
|
||||
|
||||
const Post: React.FC<{ variables: any }> = ({ variables }) => {
|
||||
const { loading, error, data } = useQuery(GET_PAGE_QUERY, {
|
||||
variables,
|
||||
// Setting this value to true will make the component rerender when
|
||||
// the "networkStatus" changes, so we are able to know if it is fetching
|
||||
// more data
|
||||
notifyOnNetworkStatusChange: true,
|
||||
});
|
||||
|
||||
if (error) return <ErrorMessage message="Error loading search results." />;
|
||||
if (loading) return <div>Loading</div>;
|
||||
|
||||
const { title, content, modified, featured_image } = data.page;
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1 className="text-3xl font-semibold text-primary my-6 inline-block">
|
||||
{title}
|
||||
</h1>
|
||||
<p className="mb-6">Edited: {modified}</p>
|
||||
<img src={featured_image} className="mb-6" alt="featured_img" />
|
||||
<div>{parse(content)}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Post;
|
||||
Reference in New Issue
Block a user