[refactor,#59][s]: move packages/portal => examples/catalog as per plan in #59.

What is currently packages/portal is example of a running portal and should move to examples (it will get replaced by an actual portal lib soon).
This commit is contained in:
Rufus Pollock
2021-03-06 17:55:32 +01:00
parent 14e6f1d597
commit 337d4a8186
77 changed files with 8 additions and 1 deletions

View File

@@ -0,0 +1,15 @@
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;

View File

@@ -0,0 +1,17 @@
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;

View File

@@ -0,0 +1,36 @@
interface TableProps {
columns: Array<any>;
data: Array<any>;
className?: string;
}
const Table: React.FC<TableProps> = ({ columns, data, className }) => {
return (
<table className={`table-auto w-full text-sm text-left my-6 ${className}`}>
<thead>
<tr>
{columns.map(({ key, name }) => (
<th key={key} className="px-4 py-2">
{name}
</th>
))}
</tr>
</thead>
<tbody>
{data.map((item) => (
<tr key={item.id}>
{columns.map(({ key, render }) => (
<td key={key} className="px-4 py-2">
{(render && typeof render === 'function' && render(item)) ||
item[key] ||
''}
</td>
))}
</tr>
))}
</tbody>
</table>
);
};
export default Table;

View File

@@ -0,0 +1,5 @@
import Table from './Table';
import ErrorMessage from './Error';
import CustomLink from './CustomLink';
export { Table, ErrorMessage, CustomLink };

View File

@@ -0,0 +1,53 @@
import { useQuery } from '@apollo/react-hooks';
import { Table, ErrorMessage } from '../_shared';
import { GET_DATAPACKAGE_QUERY } from '../../graphql/queries';
const columns = [
{
name: 'Files',
key: 'files',
render: ({ resources }) => (resources && resources.length) || 0,
},
{
name: 'Size',
key: 'size',
},
{
name: 'Format',
key: 'format',
},
{
name: 'Created',
key: 'metadata_created',
},
{
name: 'Updated',
key: 'metadata_modified',
},
{
name: 'License',
key: 'license',
},
{
name: 'Source',
key: 'source',
},
];
const About: React.FC<{ variables: any }> = ({ variables }) => {
const { loading, error, data } = useQuery(GET_DATAPACKAGE_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 <Table columns={columns} data={[result]} />;
};
export default About;

View File

@@ -0,0 +1,45 @@
import Link from 'next/link';
import { useQuery } from '@apollo/react-hooks';
import { ErrorMessage } from '../_shared';
import { GET_ORG_QUERY } from '../../graphql/queries';
const Org: 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 { organization } = data.dataset.result;
return (
<>
{organization ? (
<>
<img
src={
organization.image_url ||
'https://datahub.io/static/img/datahub-cube-edited.svg'
}
className="h-5 w-5 mr-2 inline-block"
alt="org_img"
/>
<Link href={`/@${organization.name}`}>
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
<a className="font-semibold text-primary underline">
{organization.title || organization.name}
</a>
</Link>
</>
) : (
''
)}
</>
);
};
export default Org;

View File

@@ -0,0 +1,69 @@
/* eslint-disable jsx-a11y/anchor-is-valid */
/* eslint-disable react/display-name */
import Link from 'next/link';
import { useQuery } from '@apollo/react-hooks';
import { Table, ErrorMessage } from '../_shared';
import { GET_RESOURCES_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',
},
{
name: 'Updated',
key: 'last_modified',
},
{
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_RESOURCES_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 (
<>
<h3 className="text-xl font-semibold">Data Files</h3>
<Table
columns={columns}
data={result.resources.map((resource) => ({
...resource,
parentName: result.name,
}))}
/>
</>
);
};
export default Resources;

View File

@@ -0,0 +1,65 @@
/* eslint-disable jsx-a11y/anchor-is-valid */
import Link from 'next/link';
import { useState } from 'react';
const Nav: React.FC = () => {
const [open, setOpen] = useState(false);
const handleClick = (event) => {
event.preventDefault();
setOpen(!open);
};
return (
<nav className="flex items-center justify-between flex-wrap bg-white p-4 border-b border-gray-200">
<div className="flex items-center flex-shrink-0 text-gray-700 mr-6">
<img src="/images/logo.svg" alt="portal logo" width="40" />
</div>
<div className="block lg:hidden mx-4">
<button
onClick={handleClick}
className="flex items-center px-3 py-2 border rounded text-gray-700 border-orange-400 hover:text-black hover:border-black"
>
<svg
className="fill-current h-3 w-3"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<title>Menu</title>
<path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z" />
</svg>
</button>
</div>
<div className={`${open ? `block` : `hidden`} lg:block`}>
<Link href="/blog">
<a className="block mt-4 lg:inline-block lg:mt-0 active:bg-primary-background text-gray-700 hover:text-black mr-6">
Blog
</a>
</Link>
<Link href="/search">
<a className="block mt-4 lg:inline-block lg:mt-0 text-gray-700 hover:text-black mr-6">
Search
</a>
</Link>
<a
href="http://tech.datopian.com/frontend/"
className="block mt-4 lg:inline-block lg:mt-0 text-gray-700 hover:text-black mr-6"
target="_blank"
rel="noreferrer"
>
Docs
</a>
<a
href="https://github.com/datopian/portal"
className="inline-block text-tiny px-4 py-2 leading-none border rounded text-primary bg-primary-background border-black hover:border-gray-700 hover:text-gray-700 hover:bg-white mt-4 lg:mt-0"
target="_blank"
rel="noreferrer"
>
GitHub
</a>
</div>
</nav>
);
};
export default Nav;

View File

@@ -0,0 +1,53 @@
/* eslint-disable jsx-a11y/anchor-is-valid */
import Link from 'next/link';
import { useQuery } from '@apollo/react-hooks';
import { ErrorMessage } from '../_shared';
import { SEARCH_QUERY } from '../../graphql/queries';
const Recent: 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 mx-4 lg:my-20">
<h1 className="text-2xl font-thin mb-4">Recent Datasets</h1>
<div className="recent flex flex-col lg:flex-row">
{result.results.map((dataset, index) => (
<div
key={index}
className="border px-4 mb-4 mr-3 border-gray-100 w-5/6 shadow-sm"
>
<h1 className="text-2xl font-thin">{dataset.title}</h1>
<p className="text-gray-500">
{dataset.organization && dataset.organization.description}
</p>
<Link
href={`/@${
dataset.organization ? dataset.organization.name : 'dataset'
}/${dataset.name}`}
>
<a className="pt-3 flex justify-end text-orange-500">
View Dataset
</a>
</Link>
</div>
))}
</div>
</section>
);
};
export default Recent;

View File

@@ -0,0 +1,69 @@
/* eslint-disable react/display-name */
import { useQuery } from '@apollo/react-hooks';
import { Table, ErrorMessage } from '../_shared';
import { GET_RESOURCES_QUERY } from '../../graphql/queries';
const columns = [
{
name: 'Name',
key: 'name',
render: ({ name, id }) => name || id,
},
{
name: 'Title',
key: 'title',
},
{
name: 'Description',
key: 'description',
},
{
name: 'Format',
key: 'format',
},
{
name: 'Size',
key: 'size',
},
{
name: 'Created',
key: 'created',
},
{
name: 'Updated',
key: 'last_modified',
},
{
name: 'Download',
key: 'download',
render: ({ url, format }) => (
<a
href={url}
className="bg-white hover:bg-gray-200 border text-black font-semibold py-2 px-4 rounded"
>
{format}
</a>
),
},
];
const About: React.FC<{ variables: any }> = ({ variables }) => {
const { loading, error, data } = useQuery(GET_RESOURCES_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
);
return <Table columns={columns} data={[resource]} />;
};
export default About;

View File

@@ -0,0 +1,25 @@
import { useQuery } from '@apollo/react-hooks';
import { ErrorMessage } from '../_shared';
import { GET_RESOURCES_QUERY } from '../../graphql/queries';
const DataExplorer: React.FC<{ variables: any }> = ({ variables }) => {
const { loading, error, data } = useQuery(GET_RESOURCES_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
);
return <>{JSON.stringify(resource)}</>;
};
export default DataExplorer;

View File

@@ -0,0 +1,65 @@
import { useState } from 'react';
import { useRouter } from 'next/router';
const Form: React.FC = () => {
const router = useRouter();
const [q, setQ] = useState(router.query.q);
const [sort, setSort] = useState(router.query.sort);
const handleChange = (event) => {
if (event.target.name === 'q') {
setQ(event.target.value);
} else if (event.target.name === 'sort') {
setSort(event.target.value);
}
};
const handleSubmit = (event) => {
event.preventDefault();
router.push({
pathname: '/search',
query: { q, sort },
});
};
return (
<form onSubmit={handleSubmit} className="items-center">
<div className="flex">
<input
type="text"
name="q"
value={q}
onChange={handleChange}
placeholder="Search"
aria-label="Search"
className="bg-white focus:outline-none focus:shadow-outline border border-gray-300 w-1/2 rounded-lg py-2 px-4 block appearance-none leading-normal"
/>
<button
onClick={handleSubmit}
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>
</div>
<div className="inline-block my-6 float-right">
<label htmlFor="field-order-by">Order by:</label>
<select
className="bg-white"
id="field-order-by"
name="sort"
onChange={handleChange}
onBlur={handleChange}
value={sort}
>
<option value="score:desc">Relevance</option>
<option value="title_string:asc">Name Ascending</option>
<option value="title_string:desc">Name Descending</option>
<option value="metadata_modified:desc">Last Modified</option>
<option value="views_recent:desc">Popular</option>
</select>
</div>
</form>
);
};
export default Form;

View File

@@ -0,0 +1,38 @@
/* eslint-disable jsx-a11y/anchor-is-valid */
import Link from 'next/link';
const Item: React.FC<{ datapackage: any }> = ({ datapackage }) => {
return (
<div className="mb-6">
<h3 className="text-xl font-semibold">
<Link
href={`/@${
datapackage.organization
? datapackage.organization.name
: 'dataset'
}/${datapackage.name}`}
>
<a className="text-primary">
{datapackage.title || datapackage.name}
</a>
</Link>
</h3>
<Link
href={`/@${
datapackage.organization ? datapackage.organization.name : 'dataset'
}`}
>
<a className="text-gray-500 block mt-1">
{datapackage.organization
? datapackage.organization.title
: 'dataset'}
</a>
</Link>
<div className="leading-relaxed mt-2">
{datapackage.description || datapackage.notes}
</div>
</div>
);
};
export default Item;

View File

@@ -0,0 +1,27 @@
import { useQuery } from '@apollo/react-hooks';
import Item from './Item';
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>
{result.results.map((pkg, index) => (
<Item datapackage={pkg} key={index} />
))}
</ul>
);
};
export default List;

View File

@@ -0,0 +1,26 @@
import { useQuery } from '@apollo/react-hooks';
import { ErrorMessage } from '../_shared';
import { GET_TOTAL_COUNT_QUERY } from '../../graphql/queries';
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 (
<h1 className="text-3xl font-semibold text-primary my-6 inline-block">
{result.count} results found
</h1>
);
};
export default Total;

View File

@@ -0,0 +1,39 @@
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;

View File

@@ -0,0 +1,32 @@
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;

View File

@@ -0,0 +1,32 @@
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;