[examples/openspending,home][xl]: removes datasets table, implement dataset cards grid, implement country facet
This commit is contained in:
parent
902e5e07a0
commit
8cb3cd4ddb
@ -1,15 +1,15 @@
|
|||||||
import Link from 'next/link'
|
import Link from 'next/link';
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx';
|
||||||
|
|
||||||
export function Button({ href, className = "", ...props }) {
|
export function Button({ href, className = '', ...props }) {
|
||||||
className = clsx(
|
className = clsx(
|
||||||
'inline-flex justify-center rounded-2xl bg-emerald-600 p-4 text-base font-semibold text-white hover:bg-emerald-500 focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-emerald-500 active:text-white/70',
|
'inline-flex justify-center rounded-2xl bg-emerald-600 p-4 text-base font-semibold text-white hover:bg-emerald-500 focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-emerald-500 active:text-white/70',
|
||||||
className
|
className
|
||||||
)
|
);
|
||||||
|
|
||||||
return href ? (
|
return href ? (
|
||||||
<Link href={href} className={className} {...props} />
|
<Link scroll={false} href={href} className={className} {...props} />
|
||||||
) : (
|
) : (
|
||||||
<button className={className} {...props} />
|
<button className={className} {...props} />
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
80
examples/openspending/components/DatasetCard.tsx
Normal file
80
examples/openspending/components/DatasetCard.tsx
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import Link from 'next/link';
|
||||||
|
import { Project } from '../lib/project.interface';
|
||||||
|
import ExternalLinkIcon from './icons/ExternalLinkIcon';
|
||||||
|
|
||||||
|
export default function DatasetCard({ dataset }: { dataset: Project }) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={dataset.name}
|
||||||
|
className="overflow-hidden rounded-xl border border-gray-200"
|
||||||
|
>
|
||||||
|
<Link
|
||||||
|
href=""
|
||||||
|
className="flex items-center gap-x-4 border-b border-gray-900/5 bg-gray-50 p-6"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={dataset.owner.logo || '/assets/org-icon.svg'}
|
||||||
|
alt={dataset.owner.name}
|
||||||
|
className="h-12 w-12 flex-none rounded-lg bg-white object-cover ring-1 ring-gray-900/10 p-2"
|
||||||
|
/>
|
||||||
|
<div className="text-sm font-medium leading-6">
|
||||||
|
<div className="text-gray-900 line-clamp-1">{dataset.title}</div>
|
||||||
|
<div className="text-gray-500 line-clamp-1">
|
||||||
|
{dataset.owner.title}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
<dl className="-my-3 divide-y divide-gray-100 px-6 py-4 text-sm leading-6">
|
||||||
|
<div className="flex justify-between gap-x-4 py-3">
|
||||||
|
<dt className="text-gray-500">Name</dt>
|
||||||
|
<dd className="flex items-start gap-x-2">
|
||||||
|
<div className="font-medium text-gray-900 line-clamp-1">
|
||||||
|
{dataset.name}
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between gap-x-4 py-3">
|
||||||
|
<dt className="text-gray-500">Country</dt>
|
||||||
|
<dd className="flex items-start gap-x-2">
|
||||||
|
<div className="font-medium text-gray-900">
|
||||||
|
{dataset.countryCode}
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between gap-x-4 py-3">
|
||||||
|
<dt className="text-gray-500">Fiscal Period</dt>
|
||||||
|
<dd className="text-gray-700">
|
||||||
|
{dataset.fiscalPeriod?.start && (
|
||||||
|
<time dateTime={dataset.fiscalPeriod.start}>
|
||||||
|
{dataset.fiscalPeriod.start}
|
||||||
|
</time>
|
||||||
|
)}
|
||||||
|
{dataset.fiscalPeriod?.start && (
|
||||||
|
<>
|
||||||
|
{' - '}
|
||||||
|
<time dateTime={dataset.fiscalPeriod.start}>
|
||||||
|
{dataset.fiscalPeriod.start}
|
||||||
|
</time>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between gap-x-4 py-3">
|
||||||
|
<dt className="text-gray-500">Metadata</dt>
|
||||||
|
<dd className="flex items-start gap-x-2">
|
||||||
|
<div className="font-medium text-gray-900">
|
||||||
|
<Link
|
||||||
|
// TODO: where do we get the info needed for this link?
|
||||||
|
href=""
|
||||||
|
target="_blank"
|
||||||
|
className="flex items-center hover:text-gray-700"
|
||||||
|
>
|
||||||
|
datapackage.json <ExternalLinkIcon className="ml-1" />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
19
examples/openspending/components/DatasetsGrid.tsx
Normal file
19
examples/openspending/components/DatasetsGrid.tsx
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { Project } from '../lib/project.interface';
|
||||||
|
import DatasetCard from './DatasetCard';
|
||||||
|
|
||||||
|
export default function DatasetsGrid({ datasets }: { datasets: Project[] }) {
|
||||||
|
return (
|
||||||
|
<ul
|
||||||
|
className="grid gap-x-6 gap-y-8 grid-cols-1 sm:grid-cols-2 md:grid-cols-3"
|
||||||
|
role="list"
|
||||||
|
>
|
||||||
|
{datasets.map((dataset, idx) => {
|
||||||
|
return (
|
||||||
|
<li key={`datasets-grid-item-${idx}`}>
|
||||||
|
<DatasetCard dataset={dataset} />
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
||||||
107
examples/openspending/components/DatasetsSearch.tsx
Normal file
107
examples/openspending/components/DatasetsSearch.tsx
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import { useForm } from 'react-hook-form';
|
||||||
|
import DatasetsGrid from './DatasetsGrid';
|
||||||
|
import { Project } from '../lib/project.interface';
|
||||||
|
import { Index } from 'flexsearch';
|
||||||
|
|
||||||
|
export default function DatasetsSearch({ datasets }: { datasets: Project[] }) {
|
||||||
|
const index = new Index({ tokenize: 'full' });
|
||||||
|
datasets.forEach((dataset: Project) =>
|
||||||
|
index.add(
|
||||||
|
dataset.name,
|
||||||
|
`${dataset.repo} ${dataset.name} ${dataset.title} ${dataset.author} ${dataset.title} ${dataset.cityCode} ${dataset.fiscalPeriod?.start} ${dataset.fiscalPeriod?.end}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const { register, watch, handleSubmit, reset, resetField } = useForm({
|
||||||
|
defaultValues: {
|
||||||
|
searchTerm: '',
|
||||||
|
country: '',
|
||||||
|
fiscalPeriodStart: '',
|
||||||
|
fiscalPeriodEnd: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const allCountries = datasets
|
||||||
|
.map((item) => item.countryCode)
|
||||||
|
.filter((v) => v) // Filters false values
|
||||||
|
.filter((v, i, a) => a.indexOf(v) === i) // Remove duplicates
|
||||||
|
// TODO: title should be the full name
|
||||||
|
.map((code) => ({ code, title: code }));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="flex flex-col gap-3 sm:flex-row">
|
||||||
|
<div className="min-w-0 flex-auto relative">
|
||||||
|
<input
|
||||||
|
placeholder="Search datasets"
|
||||||
|
aria-label="Hate speech on Twitter"
|
||||||
|
{...register('searchTerm')}
|
||||||
|
className="relative w-full rounded-lg bg-white py-2 pl-3 pr-10 text-left shadow-md focus:outline-none focus-visible:border-emerald-500 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-emerald-400 sm:text-sm"
|
||||||
|
/>
|
||||||
|
{watch().searchTerm !== '' && (
|
||||||
|
<button
|
||||||
|
onClick={() => resetField('searchTerm')}
|
||||||
|
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500"
|
||||||
|
>
|
||||||
|
<CloseIcon />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="sm:basis-1/6">
|
||||||
|
{/* TODO: nicer select e.g. headlessui example */}
|
||||||
|
<select
|
||||||
|
className="w-full h-full rounded-lg bg-white py-2 pl-3 pr-10 text-left shadow-md focus:outline-none focus-visible:border-emerald-500 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-emerald-400 sm:text-sm"
|
||||||
|
{...register('country')}
|
||||||
|
>
|
||||||
|
<option value="">All</option>
|
||||||
|
{allCountries.map((country) => {
|
||||||
|
return (
|
||||||
|
<option key={country.code} value={country.code}>
|
||||||
|
{country.title}
|
||||||
|
</option>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="min-w-full mt-10 align-middle">
|
||||||
|
<DatasetsGrid
|
||||||
|
datasets={datasets
|
||||||
|
.filter((dataset: Project) =>
|
||||||
|
watch().searchTerm && watch().searchTerm !== ''
|
||||||
|
? index.search(watch().searchTerm).includes(dataset.name)
|
||||||
|
: true
|
||||||
|
)
|
||||||
|
.filter((dataset) =>
|
||||||
|
watch().country && watch().country !== ''
|
||||||
|
? dataset.countryCode === watch().country
|
||||||
|
: true
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const CloseIcon = () => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<g id="Menu / Close_MD">
|
||||||
|
<path
|
||||||
|
id="Vector"
|
||||||
|
d="M18 18L12 12M12 12L6 6M12 12L18 6M12 12L6 18"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -46,7 +46,6 @@ export function Header() {
|
|||||||
</li>))}
|
</li>))}
|
||||||
</ul>
|
</ul>
|
||||||
<div className="hidden sm:mt-10 sm:flex lg:mt-0 lg:grow lg:basis-0 lg:justify-end">
|
<div className="hidden sm:mt-10 sm:flex lg:mt-0 lg:grow lg:basis-0 lg:justify-end">
|
||||||
<Button href="#">View on GitHub</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
</header >
|
</header >
|
||||||
|
|||||||
@ -23,8 +23,8 @@ export function Hero() {
|
|||||||
fiscal data in the public sphere.
|
fiscal data in the public sphere.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Button href="#" className="mt-10 w-full sm:hidden">
|
<Button href="#datasets" className="mt-10">
|
||||||
View on GitHub
|
Search datasets
|
||||||
</Button>
|
</Button>
|
||||||
<dl className="mt-10 grid grid-cols-2 gap-x-10 gap-y-6 sm:mt-16 sm:gap-x-16 sm:gap-y-10 sm:text-center lg:auto-cols-auto lg:grid-flow-col lg:grid-cols-none lg:justify-start lg:text-left">
|
<dl className="mt-10 grid grid-cols-2 gap-x-10 gap-y-6 sm:mt-16 sm:gap-x-16 sm:gap-y-10 sm:text-center lg:auto-cols-auto lg:grid-flow-col lg:grid-cols-none lg:justify-start lg:text-left">
|
||||||
{[
|
{[
|
||||||
|
|||||||
@ -1,15 +1,17 @@
|
|||||||
import { FiscalDataPackage } from './datapackage.interface';
|
import { FiscalDataPackage } from './datapackage.interface';
|
||||||
import { Project } from './project.interface';
|
import { Project } from './project.interface';
|
||||||
|
|
||||||
export function loadDataPackage(
|
export function loadDataPackage(datapackage: FiscalDataPackage, repo): Project {
|
||||||
datapackage: FiscalDataPackage,
|
|
||||||
owner: string,
|
|
||||||
repo: string
|
|
||||||
): Project {
|
|
||||||
return {
|
return {
|
||||||
name: datapackage.name,
|
name: datapackage.name,
|
||||||
owner: { name: owner },
|
title: datapackage.title,
|
||||||
repo: { name: repo },
|
owner: {
|
||||||
|
name: repo.owner.login,
|
||||||
|
logo: repo.owner.avatar_url,
|
||||||
|
// TODO: make this title work
|
||||||
|
title: repo.owner.login,
|
||||||
|
},
|
||||||
|
repo: { name: repo, full_name: repo.full_name },
|
||||||
files: datapackage.resources,
|
files: datapackage.resources,
|
||||||
author: datapackage.author ? datapackage.author : null,
|
author: datapackage.author ? datapackage.author : null,
|
||||||
cityCode: datapackage.cityCode ? datapackage.cityCode : null,
|
cityCode: datapackage.cityCode ? datapackage.cityCode : null,
|
||||||
@ -17,7 +19,8 @@ export function loadDataPackage(
|
|||||||
? (datapackage.countryCode as string)
|
? (datapackage.countryCode as string)
|
||||||
: null,
|
: null,
|
||||||
fiscalPeriod: datapackage.fiscalPeriod
|
fiscalPeriod: datapackage.fiscalPeriod
|
||||||
? { start: datapackage.fiscalPeriod.start
|
? {
|
||||||
|
start: datapackage.fiscalPeriod.start
|
||||||
? datapackage.fiscalPeriod.start
|
? datapackage.fiscalPeriod.start
|
||||||
: null,
|
: null,
|
||||||
end: datapackage.fiscalPeriod.end
|
end: datapackage.fiscalPeriod.end
|
||||||
@ -26,6 +29,6 @@ export function loadDataPackage(
|
|||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
readme: datapackage.readme ? datapackage.readme : '',
|
readme: datapackage.readme ? datapackage.readme : '',
|
||||||
datapackage
|
datapackage,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
import { FiscalDataPackage, TabularDataResource } from "./datapackage.interface";
|
import {
|
||||||
|
FiscalDataPackage,
|
||||||
|
TabularDataResource,
|
||||||
|
} from './datapackage.interface';
|
||||||
|
|
||||||
export interface Project {
|
export interface Project {
|
||||||
owner: { name: string; logo?: string }; // Info about the owner of the data repo
|
owner: { name: string; logo?: string; title?: string }; // Info about the owner of the data repo
|
||||||
repo: { name: string; logo?: string }; // Info about the the data repo
|
repo: { name: string; full_name: string }; // Info about the the data repo
|
||||||
files: TabularDataResource[];
|
files: TabularDataResource[];
|
||||||
name: string;
|
name: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
@ -14,5 +17,5 @@ export interface Project {
|
|||||||
end: string;
|
end: string;
|
||||||
};
|
};
|
||||||
readme?: string;
|
readme?: string;
|
||||||
datapackage: FiscalDataPackage
|
datapackage: FiscalDataPackage;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { GithubProject, getProjectDataPackage } from '../lib/octokit';
|
import {
|
||||||
|
GithubProject,
|
||||||
|
getProjectDataPackage,
|
||||||
|
getProjectMetadata,
|
||||||
|
} from '../lib/octokit';
|
||||||
import getConfig from 'next/config';
|
import getConfig from 'next/config';
|
||||||
import ExternalLinkIcon from '../components/icons/ExternalLinkIcon';
|
import ExternalLinkIcon from '../components/icons/ExternalLinkIcon';
|
||||||
import TimeAgo from 'react-timeago';
|
import TimeAgo from 'react-timeago';
|
||||||
@ -10,44 +14,48 @@ import { Header } from '../components/Header';
|
|||||||
import { Container } from '../components/Container';
|
import { Container } from '../components/Container';
|
||||||
import { FiscalDataPackage } from '../lib/datapackage.interface';
|
import { FiscalDataPackage } from '../lib/datapackage.interface';
|
||||||
import { loadDataPackage } from '../lib/loader';
|
import { loadDataPackage } from '../lib/loader';
|
||||||
import { Project } from '../lib/project.interface';
|
import DatasetsSearch from '../components/DatasetsSearch';
|
||||||
import { Index } from 'flexsearch';
|
|
||||||
import { useForm } from 'react-hook-form';
|
|
||||||
|
|
||||||
export async function getStaticProps() {
|
export async function getStaticProps() {
|
||||||
const jsonDirectory = path.join(process.cwd(), '/datasets.json');
|
const jsonDirectory = path.join(process.cwd(), '/datasets.json');
|
||||||
const repos = await fs.readFile(jsonDirectory, 'utf8');
|
const repos = await fs.readFile(jsonDirectory, 'utf8');
|
||||||
const github_pat = getConfig().serverRuntimeConfig.github_pat;
|
const github_pat = getConfig().serverRuntimeConfig.github_pat;
|
||||||
const datapackages = await Promise.all(
|
const datapackages = await Promise.all(
|
||||||
JSON.parse(repos).map(
|
JSON.parse(repos).map(async (_repo: GithubProject) => {
|
||||||
async (_repo: GithubProject) =>
|
const datapackage = await getProjectDataPackage(
|
||||||
await getProjectDataPackage(_repo.owner, _repo.name, 'main', github_pat)
|
_repo.owner,
|
||||||
)
|
_repo.name,
|
||||||
|
'main',
|
||||||
|
github_pat
|
||||||
|
);
|
||||||
|
const repo = await getProjectMetadata(
|
||||||
|
_repo.owner,
|
||||||
|
_repo.name,
|
||||||
|
github_pat
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
datapackage,
|
||||||
|
repo,
|
||||||
|
};
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const projects = datapackages.map(
|
const projects = datapackages.map(
|
||||||
(datapackage: FiscalDataPackage & { repo: string }) =>
|
(item: { datapackage: FiscalDataPackage & { repo: string }; repo: any }) =>
|
||||||
loadDataPackage(datapackage, 'os-data', datapackage.name)
|
loadDataPackage(item.datapackage, item.repo)
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
projects,
|
projects: JSON.stringify(projects),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Datasets({ projects }) {
|
export function Datasets({ projects }) {
|
||||||
const index = new Index({ tokenize: 'full' });
|
projects = JSON.parse(projects);
|
||||||
projects.forEach((project: Project) =>
|
|
||||||
index.add(
|
|
||||||
project.name,
|
|
||||||
`${project.repo} ${project.name} ${project.title} ${project.author} ${project.title} ${project.cityCode} ${project.fiscalPeriod?.start} ${project.fiscalPeriod?.end}`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
const { register, watch, handleSubmit, reset } = useForm({
|
|
||||||
defaultValues: {
|
|
||||||
searchTerm: '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white min-h-screen">
|
<div className="bg-white min-h-screen">
|
||||||
<Header />
|
<Header />
|
||||||
@ -65,101 +73,8 @@ export function Datasets({ projects }) {
|
|||||||
Find spending data about countries all around the world.
|
Find spending data about countries all around the world.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
<div className="mt-10">
|
||||||
<div className="mt-6 flex flex-col gap-3 sm:flex-row">
|
<DatasetsSearch datasets={projects} />
|
||||||
<input
|
|
||||||
placeholder="Search here"
|
|
||||||
aria-label="Hate speech on Twitter"
|
|
||||||
{...register('searchTerm')}
|
|
||||||
className="min-w-0 flex-auto appearance-none rounded-md border border-zinc-900/10 bg-white px-3 py-[calc(theme(spacing.2)-1px)] shadow-md shadow-zinc-800/5 placeholder:text-zinc-600 focus:border-emerald-500 focus:outline-none focus:ring-4 focus:ring-emerald-500/10 sm:text-sm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
|
||||||
<div className="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
|
|
||||||
<table className="min-w-full divide-y divide-gray-300">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
scope="col"
|
|
||||||
className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900"
|
|
||||||
>
|
|
||||||
Name
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
scope="col"
|
|
||||||
className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900"
|
|
||||||
>
|
|
||||||
Repository
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
scope="col"
|
|
||||||
className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900"
|
|
||||||
>
|
|
||||||
Author
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
scope="col"
|
|
||||||
className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900"
|
|
||||||
>
|
|
||||||
Fiscal Year
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
scope="col"
|
|
||||||
className="relative py-3.5 pl-3 pr-4 sm:pr-0"
|
|
||||||
></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody className="divide-y divide-gray-200">
|
|
||||||
{projects
|
|
||||||
.filter((project: Project) =>
|
|
||||||
watch().searchTerm && watch().searchTerm !== ''
|
|
||||||
? index
|
|
||||||
.search(watch().searchTerm)
|
|
||||||
.includes(project.name)
|
|
||||||
: true
|
|
||||||
)
|
|
||||||
.map((project: Project) => (
|
|
||||||
<tr key={project.name}>
|
|
||||||
<td className="whitespace-nowrap px-3 py-6 text-sm text-gray-500">
|
|
||||||
{project.name}
|
|
||||||
</td>
|
|
||||||
<td className="whitespace-nowrap px-3 py-6 text-sm group text-gray-500 hover:text-gray-900 transition-all duration-250">
|
|
||||||
<a
|
|
||||||
href={`https://github.com/${project.owner.name}/${project.repo.name}`}
|
|
||||||
target="_blank"
|
|
||||||
className="flex items-center"
|
|
||||||
>
|
|
||||||
@{project.owner.name}/{project.repo.name}{' '}
|
|
||||||
<ExternalLinkIcon className="ml-1" />
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td className="px-3 py-4 text-sm text-gray-500">
|
|
||||||
{project.author}
|
|
||||||
</td>
|
|
||||||
{project.fiscalPeriod ? (
|
|
||||||
<td className="whitespace-nowrap px-3 py-6 text-sm text-gray-500">
|
|
||||||
{project.fiscalPeriod.start} -{' '}
|
|
||||||
{project.fiscalPeriod.end}
|
|
||||||
</td>
|
|
||||||
) : (
|
|
||||||
<td className="whitespace-nowrap px-3 py-6 text-sm text-gray-500">
|
|
||||||
No data
|
|
||||||
</td>
|
|
||||||
)}
|
|
||||||
<td className="relative whitespace-nowrap py-6 pl-3 pr-4 text-right text-sm font-medium sm:pr-0">
|
|
||||||
<a
|
|
||||||
href={`/@${project.owner.name}/${project.repo.name}/`}
|
|
||||||
className="border border-gray-900 text-gray-900 px-4 py-2 transition-all hover:bg-gray-900 hover:text-white"
|
|
||||||
>
|
|
||||||
info
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
11
examples/openspending/public/assets/org-icon.svg
Normal file
11
examples/openspending/public/assets/org-icon.svg
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg fill="#000000" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="800px" height="800px" viewBox="0 0 120 120" enable-background="new 0 0 120 120" xml:space="preserve">
|
||||||
|
<rect x="2" y="108.1" width="116" height="11.9"/>
|
||||||
|
<rect x="6.744" y="96.582" width="104.979" height="6.543"/>
|
||||||
|
<rect x="15.288" y="38.532" width="17.639" height="52.925"/>
|
||||||
|
<rect x="50.484" y="38.532" width="17.639" height="52.925"/>
|
||||||
|
<rect x="84.33" y="38.532" width="17.639" height="52.925"/>
|
||||||
|
<polygon points="0,26.96 60,0 120,26.96 119.946,33.912 0,34.01 "/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 818 B |
Loading…
x
Reference in New Issue
Block a user