Alan turing portal (#815)
* [alan-turing-portal][m] - initial commit * [alan-turing][m] - first page with search * [alan-turing][m] - cleanup
This commit is contained in:
38
examples/alan-turing-portal/pages/_app.jsx
Normal file
38
examples/alan-turing-portal/pages/_app.jsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import { useEffect, useRef } from 'react'
|
||||
|
||||
import { Footer } from '../components/Footer'
|
||||
import { Header } from '../components/Header'
|
||||
|
||||
import '../styles/tailwind.css'
|
||||
import 'focus-visible'
|
||||
|
||||
function usePrevious(value) {
|
||||
let ref = useRef()
|
||||
|
||||
useEffect(() => {
|
||||
ref.current = value
|
||||
}, [value])
|
||||
|
||||
return ref.current
|
||||
}
|
||||
|
||||
export default function App({ Component, pageProps, router }) {
|
||||
let previousPathname = usePrevious(router.pathname)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="fixed inset-0 flex justify-center sm:px-8">
|
||||
<div className="flex w-full max-w-7xl lg:px-8">
|
||||
<div className="w-full bg-white ring-1 ring-zinc-100 dark:bg-zinc-900 dark:ring-zinc-300/20" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<Header />
|
||||
<main>
|
||||
<Component previousPathname={previousPathname} {...pageProps} />
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
60
examples/alan-turing-portal/pages/_document.jsx
Normal file
60
examples/alan-turing-portal/pages/_document.jsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import { Head, Html, Main, NextScript } from 'next/document'
|
||||
|
||||
const modeScript = `
|
||||
let darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
|
||||
updateMode()
|
||||
darkModeMediaQuery.addEventListener('change', updateModeWithoutTransitions)
|
||||
window.addEventListener('storage', updateModeWithoutTransitions)
|
||||
|
||||
function updateMode() {
|
||||
let isSystemDarkMode = darkModeMediaQuery.matches
|
||||
let isDarkMode = window.localStorage.isDarkMode === 'true' || (!('isDarkMode' in window.localStorage) && isSystemDarkMode)
|
||||
|
||||
if (isDarkMode) {
|
||||
document.documentElement.classList.add('dark')
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark')
|
||||
}
|
||||
|
||||
if (isDarkMode === isSystemDarkMode) {
|
||||
delete window.localStorage.isDarkMode
|
||||
}
|
||||
}
|
||||
|
||||
function disableTransitionsTemporarily() {
|
||||
document.documentElement.classList.add('[&_*]:!transition-none')
|
||||
window.setTimeout(() => {
|
||||
document.documentElement.classList.remove('[&_*]:!transition-none')
|
||||
}, 0)
|
||||
}
|
||||
|
||||
function updateModeWithoutTransitions() {
|
||||
disableTransitionsTemporarily()
|
||||
updateMode()
|
||||
}
|
||||
`
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<Html className="h-full antialiased" lang="en">
|
||||
<Head>
|
||||
<script dangerouslySetInnerHTML={{ __html: modeScript }} />
|
||||
<link
|
||||
rel="alternate"
|
||||
type="application/rss+xml"
|
||||
href={`${process.env.NEXT_PUBLIC_SITE_URL}/rss/feed.xml`}
|
||||
/>
|
||||
<link
|
||||
rel="alternate"
|
||||
type="application/feed+json"
|
||||
href={`${process.env.NEXT_PUBLIC_SITE_URL}/rss/feed.json`}
|
||||
/>
|
||||
</Head>
|
||||
<body className="flex h-full flex-col bg-zinc-50 dark:bg-black">
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
)
|
||||
}
|
||||
182
examples/alan-turing-portal/pages/index.jsx
Normal file
182
examples/alan-turing-portal/pages/index.jsx
Normal file
@@ -0,0 +1,182 @@
|
||||
import Head from 'next/head'
|
||||
import fs from 'fs'
|
||||
|
||||
import { Card } from '../components/Card'
|
||||
import { Container } from '../components/Container'
|
||||
import clientPromise from '@/lib/mddb'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import { Index } from 'flexsearch'
|
||||
import { useForm } from 'react-hook-form'
|
||||
|
||||
function DatasetCard({ dataset }) {
|
||||
return (
|
||||
<Card as="article">
|
||||
<Card.Title>{dataset.title}</Card.Title>
|
||||
<Card.Description>
|
||||
<span className="font-semibold">Link to publication: </span>{' '}
|
||||
<a
|
||||
className="underline transition hover:text-teal-400 dark:hover:text-teal-900 text-ellipsis"
|
||||
href={dataset['link-to-publication']}
|
||||
>
|
||||
{dataset['link-to-publication']}
|
||||
</a>
|
||||
</Card.Description>
|
||||
<Card.Description>
|
||||
<span className="font-semibold">Link to data: </span>
|
||||
<a
|
||||
className="underline transition hover:text-teal-600 dark:hover:text-teal-900 text-ellipsis"
|
||||
href={dataset['link-to-data']}
|
||||
>
|
||||
{dataset['link-to-data']}
|
||||
</a>
|
||||
</Card.Description>
|
||||
<Card.Description>
|
||||
<span className="font-semibold">Task Description: </span>
|
||||
{dataset['task-description']}
|
||||
</Card.Description>
|
||||
<Card.Description>
|
||||
<span className="font-semibold">Details of Task: </span>{' '}
|
||||
{dataset['details-of-task']}
|
||||
</Card.Description>
|
||||
<Card.Description>
|
||||
<span className="font-semibold">Size of Dataset: </span>{' '}
|
||||
{dataset['size-of-dataset']}
|
||||
</Card.Description>
|
||||
<Card.Description>
|
||||
<span className="font-semibold">Percentage Abusive: </span>
|
||||
{dataset['percentage-abusive']}%
|
||||
</Card.Description>
|
||||
<Card.Description>
|
||||
<span className="font-semibold">Language: </span>
|
||||
{dataset['language']}
|
||||
</Card.Description>
|
||||
<Card.Description>
|
||||
<span className="font-semibold">Level of Annotation: </span>
|
||||
{dataset['level-of-annotation'].join(', ')}
|
||||
</Card.Description>
|
||||
<Card.Description>
|
||||
<span className="font-semibold">Platform: </span>
|
||||
{dataset['platform'].join(', ')}
|
||||
</Card.Description>
|
||||
<Card.Description>
|
||||
<span className="font-semibold">Medium: </span>
|
||||
{dataset['medium'].join(', ')}
|
||||
</Card.Description>
|
||||
<Card.Description>
|
||||
<span className="font-semibold">Reference: </span>
|
||||
{dataset['reference']}
|
||||
</Card.Description>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
export default function Home({ datasets, indexText, availableLanguages, availablePlatforms }) {
|
||||
const index = new Index()
|
||||
datasets.forEach((dataset) => index.add(dataset.id, `${dataset.title} ${dataset['task-description']} ${dataset['details-of-task']} ${dataset['reference']}`))
|
||||
const { register, watch } = useForm({ defaultValues: {
|
||||
searchTerm: '',
|
||||
lang: '',
|
||||
platform: ''
|
||||
}})
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Hate Speech Dataset Catalogue</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Catalog of abusive language data (PLoS 2020)"
|
||||
/>
|
||||
</Head>
|
||||
<Container className="mt-9">
|
||||
<div className="max-w-2xl">
|
||||
<h1 className="text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl">
|
||||
Hate Speech Dataset Catalogue
|
||||
</h1>
|
||||
<article className="mt-6 flex flex-col gap-y-2 text-base text-zinc-600 dark:text-zinc-400">
|
||||
<ReactMarkdown>{indexText}</ReactMarkdown>
|
||||
</article>
|
||||
</div>
|
||||
</Container>
|
||||
<Container className="mt-24 md:mt-28">
|
||||
<div className="mx-auto grid max-w-xl grid-cols-1 gap-y-8 lg:max-w-none">
|
||||
<form className="rounded-2xl border border-zinc-100 px-4 py-6 sm:p-6 dark:border-zinc-700/40">
|
||||
<p className="mt-2 text-lg font-semibold text-zinc-600 dark:text-zinc-100">
|
||||
Search for datasets
|
||||
</p>
|
||||
<div className="mt-6 flex flex-col sm:flex-row gap-3">
|
||||
<input
|
||||
placeholder="Search here"
|
||||
aria-label="Hate speech on Twitter"
|
||||
required
|
||||
{...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-teal-500 focus:outline-none focus:ring-4 focus:ring-teal-500/10 dark:border-zinc-700 dark:bg-zinc-700/[0.15] dark:text-zinc-200 dark:placeholder:text-zinc-200 dark:focus:border-teal-400 dark:focus:ring-teal-400/10 sm:text-sm"
|
||||
/>
|
||||
<select
|
||||
placeholder="Language"
|
||||
defaultValue=""
|
||||
className="min-w-0 flex-auto text-zinc-600 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-400 focus:border-teal-500 focus:outline-none focus:ring-4 focus:ring-teal-500/10 dark:border-zinc-700 dark:bg-zinc-700/[0.15] dark:text-zinc-200 dark:placeholder:text-zinc-500 dark:focus:border-teal-400 dark:focus:ring-teal-400/10 sm:text-sm"
|
||||
{...register('lang')}
|
||||
>
|
||||
<option value="" disabled hidden>Filter by language</option>
|
||||
{availableLanguages.map((lang) => (
|
||||
<option key={lang} className='dark:bg-white dark:text-black' value={lang}>{lang}</option>
|
||||
))}
|
||||
</select>
|
||||
<select
|
||||
placeholder="Platform"
|
||||
defaultValue=""
|
||||
className="min-w-0 flex-auto text-zinc-600 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-400 focus:border-teal-500 focus:outline-none focus:ring-4 focus:ring-teal-500/10 dark:border-zinc-700 dark:bg-zinc-700/[0.15] dark:text-zinc-200 dark:placeholder:text-zinc-500 dark:focus:border-teal-400 dark:focus:ring-teal-400/10 sm:text-sm"
|
||||
{...register('platform')}
|
||||
>
|
||||
<option value="" disabled hidden>Filter by platform</option>
|
||||
{availablePlatforms.map((platform) => (
|
||||
<option key={platform} className='dark:bg-white dark:text-black' value={platform}>{platform}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
<div className="flex flex-col gap-16">
|
||||
{datasets
|
||||
.filter((dataset) =>
|
||||
watch().searchTerm && watch().searchTerm !== ''
|
||||
? index.search(watch().searchTerm).includes(dataset.id)
|
||||
: true
|
||||
)
|
||||
.filter((dataset) =>
|
||||
watch().lang && watch().lang !== ''
|
||||
? dataset.language === watch().lang
|
||||
: true
|
||||
)
|
||||
.filter((dataset) =>
|
||||
watch().platform && watch().platform !== ''
|
||||
? dataset.platform.includes(watch().platform)
|
||||
: true
|
||||
)
|
||||
.map((dataset) => (
|
||||
<DatasetCard key={dataset.title} dataset={dataset} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
const mddb = await clientPromise
|
||||
const allPages = await mddb.getFiles({ extensions: ['md', 'mdx'] })
|
||||
const datasets = allPages
|
||||
.filter((page) => page.url_path !== '/')
|
||||
.map((page) => ({ ...page.metadata, id: page._id }))
|
||||
const index = allPages.filter((page) => page.url_path === '/')[0]
|
||||
const source = fs.readFileSync(index.file_path, { encoding: 'utf-8' })
|
||||
const availableLanguages = [... new Set(datasets.map((dataset) => dataset.language))]
|
||||
const availablePlatforms = [... new Set(datasets.map((dataset) => dataset.platform).flat())]
|
||||
return {
|
||||
props: {
|
||||
indexText: source,
|
||||
datasets,
|
||||
availableLanguages,
|
||||
availablePlatforms,
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user