|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"extends": "next/core-web-vitals",
|
"extends": "next/core-web-vitals",
|
||||||
"rules": {
|
"rules": {
|
||||||
"@next/next/no-img-element": "off"
|
"@next/next/no-img-element": "error"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,15 @@
|
||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
images: {
|
images: {
|
||||||
unoptimized: true,
|
unoptimized: false,
|
||||||
|
remotePatterns: [
|
||||||
|
{
|
||||||
|
protocol: 'https',
|
||||||
|
hostname: 'upload.wikimedia.org',
|
||||||
|
port: '',
|
||||||
|
pathname: '/**',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
3
package-lock.json
generated
|
@ -36,6 +36,9 @@
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"tailwindcss": "^3.3.0",
|
"tailwindcss": "^3.3.0",
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aashutoshrathi/word-wrap": {
|
"node_modules/@aashutoshrathi/word-wrap": {
|
||||||
|
|
|
@ -9,6 +9,9 @@
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"format": "prettier --write ."
|
"format": "prettier --write ."
|
||||||
},
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.0.0"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tanstack/react-query": "^5.20.2",
|
"@tanstack/react-query": "^5.20.2",
|
||||||
"fnv-plus": "^1.3.1",
|
"fnv-plus": "^1.3.1",
|
||||||
|
|
Before Width: | Height: | Size: 921 KiB After Width: | Height: | Size: 907 KiB |
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 84 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 9.2 MiB After Width: | Height: | Size: 6.9 MiB |
|
@ -4,6 +4,7 @@ import { Zilla_Slab } from 'next/font/google'
|
||||||
import findDocumentsByAffiliation from './findDocumentsByAffiliation'
|
import findDocumentsByAffiliation from './findDocumentsByAffiliation'
|
||||||
import { Fragment } from 'react'
|
import { Fragment } from 'react'
|
||||||
import DocumentCard from '@/app/components/DocumentCard'
|
import DocumentCard from '@/app/components/DocumentCard'
|
||||||
|
import Image from 'next/image'
|
||||||
|
|
||||||
const zillaSlab = Zilla_Slab({ subsets: ['latin'], weight: ['500'] })
|
const zillaSlab = Zilla_Slab({ subsets: ['latin'], weight: ['500'] })
|
||||||
|
|
||||||
|
@ -12,6 +13,19 @@ export function generateStaticParams() {
|
||||||
return affiliationsList.map((shortName) => ({ shortName }))
|
return affiliationsList.map((shortName) => ({ shortName }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Description = ({ description }: Readonly<{ description: string }>) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{description.split('[linebreak]').map((d, i) => (
|
||||||
|
<>
|
||||||
|
<div className='text-lg sm:text-md font-serif'>{d}</div>
|
||||||
|
<br className='m-1' />
|
||||||
|
</>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default function Page({
|
export default function Page({
|
||||||
params,
|
params,
|
||||||
}: Readonly<{ params: { shortName: string } }>) {
|
}: Readonly<{ params: { shortName: string } }>) {
|
||||||
|
@ -23,25 +37,14 @@ export default function Page({
|
||||||
|
|
||||||
const affiliationDocuments = findDocumentsByAffiliation(shortName)
|
const affiliationDocuments = findDocumentsByAffiliation(shortName)
|
||||||
|
|
||||||
const Description = () => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{description.split('[linebreak]').map((d, i) => (
|
|
||||||
<>
|
|
||||||
<div className='text-lg sm:text-md font-serif'>{d}</div>
|
|
||||||
<br className='m-1' />
|
|
||||||
</>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className='grid grid-cols-1 max-w-3xl mx-auto'>
|
<div className='grid grid-cols-1 max-w-3xl mx-auto'>
|
||||||
<div className='mx-auto mb-4 max-w-3xl md:w-auto md:h-[40vw] lg:h-[20vw] rounded-lg shadow-lg shadow-slate-400'>
|
<div className='mx-auto mb-4 max-w-3xl md:w-auto md:h-[40vw] lg:h-[20vw] rounded-lg shadow-lg shadow-slate-400'>
|
||||||
<img
|
<Image
|
||||||
alt='profile'
|
alt='profile'
|
||||||
|
width={1000}
|
||||||
|
height={1000}
|
||||||
className='rounded-lg mx-auto p-8 object-cover w-full h-full'
|
className='rounded-lg mx-auto p-8 object-cover w-full h-full'
|
||||||
src={image}
|
src={image}
|
||||||
/>
|
/>
|
||||||
|
@ -57,7 +60,7 @@ export default function Page({
|
||||||
<div className='max-w-3xl mx-auto grid grid-cols-1'>
|
<div className='max-w-3xl mx-auto grid grid-cols-1'>
|
||||||
<hr className='mx-auto w-full h-1 border-0 bg-slate-200 my-2 rounded-md' />
|
<hr className='mx-auto w-full h-1 border-0 bg-slate-200 my-2 rounded-md' />
|
||||||
<br />
|
<br />
|
||||||
<Description />
|
<Description description={description} />
|
||||||
</div>
|
</div>
|
||||||
{affiliationDocuments.length > 0 && (
|
{affiliationDocuments.length > 0 && (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { notFound } from 'next/navigation'
|
||||||
import { Zilla_Slab } from 'next/font/google'
|
import { Zilla_Slab } from 'next/font/google'
|
||||||
import getAffiliations from './getAffiliations'
|
import getAffiliations from './getAffiliations'
|
||||||
import { Fragment } from 'react'
|
import { Fragment } from 'react'
|
||||||
|
import Image from 'next/image'
|
||||||
|
|
||||||
const zillaSlab = Zilla_Slab({ subsets: ['latin'], weight: ['500'] })
|
const zillaSlab = Zilla_Slab({ subsets: ['latin'], weight: ['500'] })
|
||||||
|
|
||||||
|
@ -24,8 +25,10 @@ const AffiliationCard = ({
|
||||||
<div className='m-4'>
|
<div className='m-4'>
|
||||||
<div className='grid grid-cols-1 max-w-3xl mx-auto'>
|
<div className='grid grid-cols-1 max-w-3xl mx-auto'>
|
||||||
<div className='mx-auto mb-4 max-w-3xl md:w-auto md:h-[40vw] lg:h-[20vw] rounded-lg shadow-lg shadow-slate-400'>
|
<div className='mx-auto mb-4 max-w-3xl md:w-auto md:h-[40vw] lg:h-[20vw] rounded-lg shadow-lg shadow-slate-400'>
|
||||||
<img
|
<Image
|
||||||
alt='profile'
|
alt='profile'
|
||||||
|
width={1000}
|
||||||
|
height={1000}
|
||||||
className='rounded-lg mx-auto p-8 object-cover w-full h-full'
|
className='rounded-lg mx-auto p-8 object-cover w-full h-full'
|
||||||
src={image}
|
src={image}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,15 +1,123 @@
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { Fragment, Suspense } from 'react'
|
import { Fragment, Suspense } from 'react'
|
||||||
import { affiliations, nationalities, authors } from '../../db/data'
|
import { Author, affiliations, authors } from '../../db/data'
|
||||||
import { Zilla_Slab } from 'next/font/google'
|
import { Zilla_Slab } from 'next/font/google'
|
||||||
import { notFound } from 'next/navigation'
|
import { notFound } from 'next/navigation'
|
||||||
import DocumentCard from '@/app/components/DocumentCard'
|
import DocumentCard from '@/app/components/DocumentCard'
|
||||||
import findDocumentsByAuthor from './findDocumentsByAuthor'
|
import findDocumentsByAuthor from './findDocumentsByAuthor'
|
||||||
import cardEffects from '@/app/styles/cardEffects.module.css'
|
import cardEffects from '@/app/styles/cardEffects.module.css'
|
||||||
import KonamiSnowfall from './KonamiSnowfall'
|
import KonamiSnowfall from './KonamiSnowfall'
|
||||||
|
import Image from 'next/image'
|
||||||
|
|
||||||
const zillaSlab = Zilla_Slab({ subsets: ['latin'], weight: ['500'] })
|
const zillaSlab = Zilla_Slab({ subsets: ['latin'], weight: ['500'] })
|
||||||
|
|
||||||
|
const MainAuthorPosition = ({ author }: { author: Author }) => {
|
||||||
|
const { name, affiliation, website } = author
|
||||||
|
|
||||||
|
const mainAffiliationShort = affiliation[0].split('@')[1]
|
||||||
|
const mainPosition = affiliation[0].split('@')[0]
|
||||||
|
const mainAffiliation = affiliations[mainAffiliationShort]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<span>{mainPosition} at </span>
|
||||||
|
<Link href={`/affiliation/${mainAffiliationShort}`}>
|
||||||
|
{mainAffiliation.name}
|
||||||
|
</Link>
|
||||||
|
{website ? (
|
||||||
|
<div className='mt-2'>
|
||||||
|
Visit {name.nickname ? name.nickname : name.first} at:{' '}
|
||||||
|
<a href={website} target='_blank'>
|
||||||
|
{website}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
<div className='my-4 max-h-16 flex flex-wrap gap-2'>
|
||||||
|
{affiliation.map((a: string) => (
|
||||||
|
<Link
|
||||||
|
key={a}
|
||||||
|
href={`/affiliation/${a.split('@')[1]}`}
|
||||||
|
className={`${cardEffects['card-small']} rounded-md`}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={affiliations[a.split('@')[1]].image}
|
||||||
|
alt={affiliations[a.split('@')[1]].name}
|
||||||
|
width={100}
|
||||||
|
height={100}
|
||||||
|
className='h-16 rounded-md p-2 object-contain'
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const OtherAuthorPositions = ({ author }: { author: Author }) => {
|
||||||
|
const { affiliation } = author
|
||||||
|
|
||||||
|
if (affiliation.length === 1) return
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h1 className='text-3xl md:mt-6 mt-4 mb-2 font-serif'>Other Positions</h1>
|
||||||
|
{affiliation.slice(1).map((a: string, i: number) => {
|
||||||
|
const position = a.split('@')[0]
|
||||||
|
const affiliation = affiliations[a.split('@')[1]].name
|
||||||
|
return (
|
||||||
|
<Fragment key={`${position}@${affiliation}`}>
|
||||||
|
<span className='text-slate-500 text-lg'>
|
||||||
|
{position} at{' '}
|
||||||
|
<Link href={`/affiliation/${a.split('@')[1]}`}>
|
||||||
|
{affiliation}
|
||||||
|
</Link>
|
||||||
|
</span>
|
||||||
|
</Fragment>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const FormerAuthorPositions = ({ author }: { author: Author }) => {
|
||||||
|
const { formerAffiliations } = author
|
||||||
|
|
||||||
|
if (!formerAffiliations) return null
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h1 className='text-3xl md:mt-6 mt-4 mb-2 font-serif'>
|
||||||
|
Former Positions
|
||||||
|
</h1>
|
||||||
|
{formerAffiliations?.map((a: string, i: number) => {
|
||||||
|
const position = a.split('@')[0]
|
||||||
|
const affiliation = affiliations[a.split('@')[1]].name
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment key={`${position}@${affiliation}`}>
|
||||||
|
<span className='text-slate-500 text-lg'>
|
||||||
|
{position} at{' '}
|
||||||
|
<Link href={`/affiliation/${a.split('@')[1]}`}>
|
||||||
|
{affiliation}
|
||||||
|
</Link>
|
||||||
|
</span>
|
||||||
|
</Fragment>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const AuthorBio = ({ author }: { author: Author }) => {
|
||||||
|
const { bio } = author
|
||||||
|
if (!bio) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h1 className='text-3xl md:mt-6 mt-4 mb-2 font-serif'>Bio</h1>
|
||||||
|
<p className='mb-2'>{bio}</p>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default function AuthorDisplay({
|
export default function AuthorDisplay({
|
||||||
author,
|
author,
|
||||||
}: Readonly<{ author: string }>) {
|
}: Readonly<{ author: string }>) {
|
||||||
|
@ -18,112 +126,10 @@ export default function AuthorDisplay({
|
||||||
notFound()
|
notFound()
|
||||||
}
|
}
|
||||||
|
|
||||||
const { name, affiliation, image, nationality, formerAffiliations } = data
|
const { name, image, nationality } = data
|
||||||
|
|
||||||
const authorsDocuments = findDocumentsByAuthor(author)
|
const authorsDocuments = findDocumentsByAuthor(author)
|
||||||
|
|
||||||
const MainPosition = () => {
|
|
||||||
const mainAffiliationShort = affiliation[0].split('@')[1]
|
|
||||||
const mainPosition = affiliation[0].split('@')[0]
|
|
||||||
const mainAffiliation = affiliations[mainAffiliationShort]
|
|
||||||
const { website } = data
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<span>{mainPosition} at </span>
|
|
||||||
<Link href={`/affiliation/${mainAffiliationShort}`}>
|
|
||||||
{mainAffiliation.name}
|
|
||||||
</Link>
|
|
||||||
{website ? (
|
|
||||||
<div className='mt-2'>
|
|
||||||
Visit {name.nickname ? name.nickname : name.first} at:{' '}
|
|
||||||
<a href={website} target='_blank'>
|
|
||||||
{website}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
<div className='my-4 max-h-16 flex flex-wrap gap-2'>
|
|
||||||
{affiliation.map((a: string) => (
|
|
||||||
<Link
|
|
||||||
key={a}
|
|
||||||
href={`/affiliation/${a.split('@')[1]}`}
|
|
||||||
className={`${cardEffects['card-small']} rounded-md`}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={affiliations[a.split('@')[1]].image}
|
|
||||||
alt={affiliations[a.split('@')[1]].name}
|
|
||||||
className='h-16 rounded-md p-2'
|
|
||||||
/>
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const OtherPositions = () => {
|
|
||||||
if (affiliation.length === 1) return
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<h1 className='text-3xl md:mt-6 mt-4 mb-2 font-serif'>
|
|
||||||
Other Positions
|
|
||||||
</h1>
|
|
||||||
{affiliation.slice(1).map((a: string, i: number) => {
|
|
||||||
const position = a.split('@')[0]
|
|
||||||
const affiliation = affiliations[a.split('@')[1]].name
|
|
||||||
return (
|
|
||||||
<Fragment key={`${position}@${affiliation}`}>
|
|
||||||
<span className='text-slate-500 text-lg'>
|
|
||||||
{position} at{' '}
|
|
||||||
<Link href={`/affiliation/${a.split('@')[1]}`}>
|
|
||||||
{affiliation}
|
|
||||||
</Link>
|
|
||||||
</span>
|
|
||||||
</Fragment>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const FormerPositions = () => {
|
|
||||||
if (!formerAffiliations) return null
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<h1 className='text-3xl md:mt-6 mt-4 mb-2 font-serif'>
|
|
||||||
Former Positions
|
|
||||||
</h1>
|
|
||||||
{formerAffiliations?.map((a: string, i: number) => {
|
|
||||||
const position = a.split('@')[0]
|
|
||||||
const affiliation = affiliations[a.split('@')[1]].name
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Fragment key={`${position}@${affiliation}`}>
|
|
||||||
<span className='text-slate-500 text-lg'>
|
|
||||||
{position} at{' '}
|
|
||||||
<Link href={`/affiliation/${a.split('@')[1]}`}>
|
|
||||||
{affiliation}
|
|
||||||
</Link>
|
|
||||||
</span>
|
|
||||||
</Fragment>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const Bio = () => {
|
|
||||||
const { bio } = data
|
|
||||||
if (!bio) return null
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<h1 className='text-3xl md:mt-6 mt-4 mb-2 font-serif'>Bio</h1>
|
|
||||||
<p className='mb-2'>{bio}</p>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Suspense>
|
<Suspense>
|
||||||
|
@ -131,9 +137,11 @@ export default function AuthorDisplay({
|
||||||
</Suspense>
|
</Suspense>
|
||||||
<div className='grid grid-cols-1 md:grid-cols-2 items-center max-w-3xl mx-auto'>
|
<div className='grid grid-cols-1 md:grid-cols-2 items-center max-w-3xl mx-auto'>
|
||||||
<div className='aspect-square w-[60vw] md:w-[30vw] lg:w-[20vw] 2xl:w-[15vw] overflow-hidden mx-auto mb-4'>
|
<div className='aspect-square w-[60vw] md:w-[30vw] lg:w-[20vw] 2xl:w-[15vw] overflow-hidden mx-auto mb-4'>
|
||||||
<img
|
<Image
|
||||||
alt='profile'
|
alt='profile'
|
||||||
className='rounded-full mx-auto object-cover w-full h-full border-slate-800 border-4'
|
className='rounded-full mx-auto object-cover w-full h-full border-slate-800 border-4'
|
||||||
|
width={500}
|
||||||
|
height={500}
|
||||||
src={image}
|
src={image}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -146,16 +154,16 @@ export default function AuthorDisplay({
|
||||||
{name.nickname ? ` "${name.nickname}"` : null} {name.last}
|
{name.nickname ? ` "${name.nickname}"` : null} {name.last}
|
||||||
</span>
|
</span>
|
||||||
<div className='text-slate-600 text-md sm:text-lg mt-4'>
|
<div className='text-slate-600 text-md sm:text-lg mt-4'>
|
||||||
<MainPosition />
|
<MainAuthorPosition author={data} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='max-w-3xl mx-auto grid grid-cols-1 justify-center'>
|
<div className='max-w-3xl mx-auto grid grid-cols-1 justify-center'>
|
||||||
<hr className='mx-auto w-full h-1 border-0 bg-slate-200 my-2 rounded-md' />
|
<hr className='mx-auto w-full h-1 border-0 bg-slate-200 my-2 rounded-md' />
|
||||||
<OtherPositions />
|
<OtherAuthorPositions author={data} />
|
||||||
<FormerPositions />
|
<FormerAuthorPositions author={data} />
|
||||||
<Bio />
|
<AuthorBio author={data} />
|
||||||
{authorsDocuments.length > 0 && (
|
{authorsDocuments.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<hr className='mx-auto w-full h-1 border-0 bg-slate-200 my-2 rounded-md mt-8' />
|
<hr className='mx-auto w-full h-1 border-0 bg-slate-200 my-2 rounded-md mt-8' />
|
||||||
|
|
|
@ -1,9 +1,28 @@
|
||||||
'use client'
|
'use client'
|
||||||
import Konami from 'react-konami-code'
|
import Konami from 'react-konami-code'
|
||||||
import { Snowfall } from 'react-snowfall'
|
import { Snowfall } from 'react-snowfall'
|
||||||
import { useEffect, useState } from 'react'
|
import { Fragment, useEffect, useState } from 'react'
|
||||||
import { nationalities } from '@/app/db/data'
|
import { nationalities } from '@/app/db/data'
|
||||||
import { Fragment } from 'react'
|
import NextImage from 'next/image'
|
||||||
|
|
||||||
|
const NationalityDisplay = ({
|
||||||
|
nationality,
|
||||||
|
}: Readonly<{ nationality: string }>) => {
|
||||||
|
const nationalityData = nationalities[nationality]
|
||||||
|
const { demonym, flag } = nationalityData
|
||||||
|
return (
|
||||||
|
<div className='flex items-center'>
|
||||||
|
<NextImage
|
||||||
|
src={flag}
|
||||||
|
width={100}
|
||||||
|
height={100}
|
||||||
|
className='w-10 shadow-md shadow-slate-300'
|
||||||
|
alt={`${demonym} flag`}
|
||||||
|
/>
|
||||||
|
<span className='mx-3 font-semibold'>{demonym}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default function KonamiSnowfall({
|
export default function KonamiSnowfall({
|
||||||
nationalityList,
|
nationalityList,
|
||||||
|
@ -28,23 +47,6 @@ export default function KonamiSnowfall({
|
||||||
setImages(imagesTemp)
|
setImages(imagesTemp)
|
||||||
}, [nationalityList])
|
}, [nationalityList])
|
||||||
|
|
||||||
const NationalityDisplay = ({
|
|
||||||
nationality,
|
|
||||||
}: Readonly<{ nationality: string }>) => {
|
|
||||||
const nationalityData = nationalities[nationality]
|
|
||||||
const { demonym, flag } = nationalityData
|
|
||||||
return (
|
|
||||||
<div className='flex items-center'>
|
|
||||||
<img
|
|
||||||
src={flag}
|
|
||||||
className='w-10 shadow-md shadow-slate-300'
|
|
||||||
alt={`${demonym} flag`}
|
|
||||||
/>
|
|
||||||
<span className='mx-3 font-semibold'>{demonym}</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Konami action={handleKonami} />
|
<Konami action={handleKonami} />
|
||||||
|
|
|
@ -93,32 +93,33 @@ export const Authors = ({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ReviewerDisplay = ({ r }: Readonly<{ r: reviewer }>) => {
|
||||||
|
if (r.profile) {
|
||||||
|
return (
|
||||||
|
<Link href={`/author/${r.profile}`} target='_blank'>
|
||||||
|
{r.first} {r.last}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (r.url) {
|
||||||
|
return (
|
||||||
|
<a href={r.url} target='_blank'>
|
||||||
|
{r.first} {r.last}
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
{r.first} {r.last}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export const Reviewers = ({
|
export const Reviewers = ({
|
||||||
reviewers,
|
reviewers,
|
||||||
showTitle = true,
|
showTitle = true,
|
||||||
}: Readonly<{ reviewers: reviewer[] | undefined; showTitle?: boolean }>) => {
|
}: Readonly<{ reviewers: reviewer[] | undefined; showTitle?: boolean }>) => {
|
||||||
if (!reviewers) return null
|
if (!reviewers) return null
|
||||||
const ReviewerDisplay = ({ r }: Readonly<{ r: reviewer }>) => {
|
|
||||||
if (r.profile) {
|
|
||||||
return (
|
|
||||||
<Link href={`/author/${r.profile}`} target='_blank'>
|
|
||||||
{r.first} {r.last}
|
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (r.url) {
|
|
||||||
return (
|
|
||||||
<a href={r.url} target='_blank'>
|
|
||||||
{r.first} {r.last}
|
|
||||||
</a>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
{r.first} {r.last}
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// these variants of the data display compnents are designed for client side components
|
// these variants of the data display components are designed for client side components
|
||||||
// and fetch data asynchronously
|
// and fetch data asynchronously
|
||||||
import { loadAllTopics, loadAllAuthors } from '@/app/db/loaders'
|
import { loadAllTopics, loadAllAuthors } from '@/app/db/loaders'
|
||||||
import { useSuspenseQuery } from '@tanstack/react-query'
|
import { useSuspenseQuery } from '@tanstack/react-query'
|
||||||
|
|
|
@ -13,9 +13,8 @@ const DocumentCard = ({ doc, href }: { doc: Document; href: string }) => {
|
||||||
const { title, authors, topics, dates, status, type } = manifest
|
const { title, authors, topics, dates, status, type } = manifest
|
||||||
return (
|
return (
|
||||||
<Link href={href} className='no-link-style'>
|
<Link href={href} className='no-link-style'>
|
||||||
<div
|
<button
|
||||||
className={`${cardEffects['card-large']} border-4 rounded-lg border-gray-300 hover:border-blue-500 p-5 my-4 w-full cursor-pointer shadow-slate-300 shadow-md`}
|
className={`${cardEffects['card-large']} border-4 rounded-lg border-gray-300 hover:border-blue-500 p-5 my-4 w-full cursor-pointer shadow-slate-300 shadow-md text-left`}
|
||||||
role='button' // this is a critical DEI concern as we have marked this element as a button with ARIA role, yet we have not supported button accessiblity features
|
|
||||||
>
|
>
|
||||||
<h2 className={`${zillaSlab.className} text-3xl`}>{title}</h2>
|
<h2 className={`${zillaSlab.className} text-3xl`}>{title}</h2>
|
||||||
<p className='text-slate-500 py-2 text-md mt-2'>
|
<p className='text-slate-500 py-2 text-md mt-2'>
|
||||||
|
@ -34,7 +33,7 @@ const DocumentCard = ({ doc, href }: { doc: Document; href: string }) => {
|
||||||
<p className='py-2 text-md text-slate-700 font-serif text-lg text-balance'>
|
<p className='py-2 text-md text-slate-700 font-serif text-lg text-balance'>
|
||||||
{abstract.substring(0, 500) + (abstract.length > 500 ? '...' : '')}
|
{abstract.substring(0, 500) + (abstract.length > 500 ? '...' : '')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</button>
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import styles from './loading.module.css'
|
||||||
const LoadingBar = () => {
|
const LoadingBar = () => {
|
||||||
return (
|
return (
|
||||||
<div className='w-full fixed top-0 left-0'>
|
<div className='w-full fixed top-0 left-0'>
|
||||||
<div className='h-1.5 w-full bg-pink-100 overflow-hidden'>
|
<div className='h-1.5 w-full bg-blue-100 overflow-hidden'>
|
||||||
<div
|
<div
|
||||||
className={`${styles.progress} w-full h-full bg-blue-500 ${styles['left-right']}`}
|
className={`${styles.progress} w-full h-full bg-blue-500 ${styles['left-right']}`}
|
||||||
></div>
|
></div>
|
||||||
|
|
|
@ -55,7 +55,7 @@ export default function MobileMenu() {
|
||||||
>
|
>
|
||||||
open-source contributors
|
open-source contributors
|
||||||
</a>
|
</a>
|
||||||
.
|
{'.'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import styles from './container.module.css'
|
|
||||||
/**
|
/**
|
||||||
* Renders a container component with the specified width, containing the provided children.
|
* Renders a container component with the specified width, containing the provided children.
|
||||||
*
|
*
|
||||||
|
@ -8,9 +7,5 @@ import styles from './container.module.css'
|
||||||
export default function Container({
|
export default function Container({
|
||||||
children,
|
children,
|
||||||
}: Readonly<{ children: React.ReactNode }>) {
|
}: Readonly<{ children: React.ReactNode }>) {
|
||||||
return (
|
return <div className={`pb-10 px-5 max-w-[1200px] mx-auto`}>{children}</div>
|
||||||
<div className={`${styles.container} pb-10 px-5 max-w-[1200px] mx-auto`}>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
/* @media only screen and (min-width: 48em) {
|
|
||||||
.container {
|
|
||||||
min-height: calc(100vh - 400px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: 48em) {
|
|
||||||
.container {
|
|
||||||
min-height: 400px;
|
|
||||||
}
|
|
||||||
} */
|
|
|
@ -366,7 +366,7 @@ On Day 1 we will discuss the ins and outs of the robot. The Electrical Sub team
|
||||||
'cifar 10',
|
'cifar 10',
|
||||||
'distance based cost',
|
'distance based cost',
|
||||||
'computer vision foundation',
|
'computer vision foundation',
|
||||||
'study of cost smoothnes',
|
'study of cost smoothness',
|
||||||
'adversarial attacks',
|
'adversarial attacks',
|
||||||
],
|
],
|
||||||
latest: 1,
|
latest: 1,
|
||||||
|
@ -416,7 +416,7 @@ export const topics: Readonly<{ [key: string]: Topic }> = {
|
||||||
eecs: {
|
eecs: {
|
||||||
name: 'Electrical Engineering and Computer Science',
|
name: 'Electrical Engineering and Computer Science',
|
||||||
description:
|
description:
|
||||||
'Electrical engineering and computer science are fields that combine engineering, science, and computing. The acronym EECS is derived from ther world-renowned electrical engineering and computer science program at the University of California, Berkeley.',
|
'Electrical engineering and computer science are fields that combine engineering, science, and computing. The acronym EECS is derived from their world-renowned electrical engineering and computer science program at the University of California, Berkeley.',
|
||||||
wiki: 'https://en.wikipedia.org/wiki/Computer_science_and_engineering',
|
wiki: 'https://en.wikipedia.org/wiki/Computer_science_and_engineering',
|
||||||
},
|
},
|
||||||
ai: {
|
ai: {
|
||||||
|
@ -453,7 +453,7 @@ authorName (as a slug): {
|
||||||
|
|
||||||
image: image url, can store in public/eexiv/img/profiles or link to a web resource
|
image: image url, can store in public/eexiv/img/profiles or link to a web resource
|
||||||
|
|
||||||
nationality: an array of ISO 3 letter country codes corresponding to your nationalities or ethniciities
|
nationality: an array of ISO 3 letter country codes corresponding to your nationalities
|
||||||
|
|
||||||
formerAffiliations: an array of former affiliation "slugs." they should also correspond to affiliations
|
formerAffiliations: an array of former affiliation "slugs." they should also correspond to affiliations
|
||||||
in the affiliations data
|
in the affiliations data
|
||||||
|
@ -543,7 +543,7 @@ export const authors: Readonly<{ [key: string]: Author }> = {
|
||||||
},
|
},
|
||||||
affiliation: ['Vision Researcher@1280-eecs', 'Student@srvhs'],
|
affiliation: ['Vision Researcher@1280-eecs', 'Student@srvhs'],
|
||||||
image: '/img/profiles/gostler.jpg',
|
image: '/img/profiles/gostler.jpg',
|
||||||
nationality: ['usa'],
|
nationality: ['fra', 'usa'],
|
||||||
website: 'https://github.com/gavinostler',
|
website: 'https://github.com/gavinostler',
|
||||||
bio: `I'm Gavin, a high school student from the Bay Area. I am a fullstack developer and love making random things to fill my day. I'm interested in creating useful tools and software in the future.`,
|
bio: `I'm Gavin, a high school student from the Bay Area. I am a fullstack developer and love making random things to fill my day. I'm interested in creating useful tools and software in the future.`,
|
||||||
},
|
},
|
||||||
|
@ -884,7 +884,7 @@ Raid Zero's influence extends beyond the technical achievements in robotics comp
|
||||||
name: `Team 1280, the Ragin' C-Biscuits`,
|
name: `Team 1280, the Ragin' C-Biscuits`,
|
||||||
short: 'Team 1280',
|
short: 'Team 1280',
|
||||||
image: '/img/logos/1280-main.png',
|
image: '/img/logos/1280-main.png',
|
||||||
description: `We are the San Ramon Valley High School Robotics Team (FRC Team 1280) and we have been competing in the FIRST Robotics Challenge for 16 years. With just 6 weeks to design, build, program, and fundraise for a robot, FRC teaches us teamwork, business, engineering, machinery, and computer design. We are a team of over 50 students with 1 full time mentor, 2 part time mentors, and 1 staff liason.
|
description: `We are the San Ramon Valley High School Robotics Team (FRC Team 1280) and we have been competing in the FIRST Robotics Challenge for 16 years. With just 6 weeks to design, build, program, and fundraise for a robot, FRC teaches us teamwork, business, engineering, machinery, and computer design. We are a team of over 50 students with 1 full time mentor, 2 part time mentors, and 1 staff liaison.
|
||||||
[linebreak]
|
[linebreak]
|
||||||
Throughout our FRC career, we have won several regional events and numerous awards including: the Rookie Inspiration Award, both the Radio Shack and Rockwell Automation Innovation in Control Awards, the Imagery Award, the Engineering Excellence Award, and the Creativity Award. While we do focus on the competitive aspect of robotics, we also strive to spread the knowledge of STEM through our outreach programs to those who might not otherwise have access to these opportunities.
|
Throughout our FRC career, we have won several regional events and numerous awards including: the Rookie Inspiration Award, both the Radio Shack and Rockwell Automation Innovation in Control Awards, the Imagery Award, the Engineering Excellence Award, and the Creativity Award. While we do focus on the competitive aspect of robotics, we also strive to spread the knowledge of STEM through our outreach programs to those who might not otherwise have access to these opportunities.
|
||||||
[linebreak]
|
[linebreak]
|
||||||
|
|
|
@ -23,7 +23,7 @@ const checkIsStringArray = (data: unknown): data is string[] => {
|
||||||
|
|
||||||
onmessage = (e) => {
|
onmessage = (e) => {
|
||||||
let authorIds: string[] = []
|
let authorIds: string[] = []
|
||||||
checkIsStringArray(e.data) && (authorIds = e.data as string[])
|
checkIsStringArray(e.data) && (authorIds = e.data)
|
||||||
let results = getAuthorsById(authorIds)
|
let results = getAuthorsById(authorIds)
|
||||||
|
|
||||||
postMessage(results)
|
postMessage(results)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* stylesheet for the eexiv UI and the base layout (search, logo, header, footer) */
|
/* stylesheet for the eeXiv UI and the base layout (search, logo, header, footer) */
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -35,7 +35,6 @@
|
||||||
|
|
||||||
.footerContent {
|
.footerContent {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 150px;
|
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
background-color: #c4840c;
|
background-color: #c4840c;
|
||||||
color: white;
|
color: white;
|
||||||
|
|
|
@ -9,6 +9,7 @@ import MobileMenu from './components/MobileMenu'
|
||||||
import { ToastContainer } from 'react-toastify'
|
import { ToastContainer } from 'react-toastify'
|
||||||
import 'react-toastify/dist/ReactToastify.css'
|
import 'react-toastify/dist/ReactToastify.css'
|
||||||
import Providers from './providers'
|
import Providers from './providers'
|
||||||
|
import Image from 'next/legacy/image'
|
||||||
|
|
||||||
/* The default font is Inter. If you want to use Zilla Slab (or any other Google Font,
|
/* The default font is Inter. If you want to use Zilla Slab (or any other Google Font,
|
||||||
which are pre-provided by Next.js in the 'next/font/google' module), you need to
|
which are pre-provided by Next.js in the 'next/font/google' module), you need to
|
||||||
|
@ -37,8 +38,11 @@ export default function RootLayout({
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
<div className='max-w-[1200px] flex flex-nowrap mx-auto justify-between items-center'>
|
<div className='max-w-[1200px] flex flex-nowrap mx-auto justify-between items-center'>
|
||||||
<Link href='/affiliation/1280-eecs'>
|
<Link href='/affiliation/1280-eecs'>
|
||||||
<img
|
<Image
|
||||||
className='h-[100px] mt-4'
|
className='mt-4'
|
||||||
|
layout='intrinsic'
|
||||||
|
width={244}
|
||||||
|
height={100}
|
||||||
src='/img/logos/eecs-wordmark.png'
|
src='/img/logos/eecs-wordmark.png'
|
||||||
alt='EECS'
|
alt='EECS'
|
||||||
/>
|
/>
|
||||||
|
@ -52,7 +56,7 @@ export default function RootLayout({
|
||||||
>
|
>
|
||||||
open-source contributors
|
open-source contributors
|
||||||
</a>
|
</a>
|
||||||
.
|
{'.'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { documents, authors, affiliations, Author } from './db/data'
|
import {
|
||||||
|
documents,
|
||||||
|
authors,
|
||||||
|
affiliations,
|
||||||
|
Author,
|
||||||
|
Affiliation,
|
||||||
|
} from './db/data'
|
||||||
import News from './components/News'
|
import News from './components/News'
|
||||||
import RandomDocs from './components/RandomDocs'
|
import RandomDocs from './components/RandomDocs'
|
||||||
import RecentDocuments from './components/RecentDocuments'
|
import RecentDocuments from './components/RecentDocuments'
|
||||||
|
@ -35,19 +41,19 @@ function sortAuthorsByDocumentsPublished(authors: {
|
||||||
return authorsWithId.map(({ id, author }) => ({ id, author }))
|
return authorsWithId.map(({ id, author }) => ({ id, author }))
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Home() {
|
interface AuthorDisplayProps {
|
||||||
const AuthorDisplay = () => {
|
authors: { [key: string]: Author }
|
||||||
let i = 0
|
affiliations: { [key: string]: Affiliation }
|
||||||
return (
|
}
|
||||||
<ol className='list-decimal pl-4 space-y-2'>
|
const AuthorDisplay = ({ authors, affiliations }: AuthorDisplayProps) => {
|
||||||
{sortAuthorsByDocumentsPublished(authors).map(({ id, author }) => {
|
return (
|
||||||
|
<ol className='list-decimal pl-4 space-y-2'>
|
||||||
|
{sortAuthorsByDocumentsPublished(authors)
|
||||||
|
.slice(0, 10)
|
||||||
|
.map(({ id, author }) => {
|
||||||
let data = author
|
let data = author
|
||||||
|
|
||||||
let affiliationSlug = data.affiliation[0].split('@')[1]
|
let affiliationSlug = data.affiliation[0].split('@')[1]
|
||||||
let affiliation = affiliations[affiliationSlug]
|
let affiliation = affiliations[affiliationSlug]
|
||||||
i++
|
|
||||||
|
|
||||||
if (i > 10) return
|
|
||||||
return (
|
return (
|
||||||
<li key={id}>
|
<li key={id}>
|
||||||
<Link href={`/author/${id}`}>
|
<Link href={`/author/${id}`}>
|
||||||
|
@ -62,10 +68,11 @@ export default function Home() {
|
||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</ol>
|
</ol>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<div className='text-slate-800 flex flex-wrap md:flex-row justify-center'>
|
<div className='text-slate-800 flex flex-wrap md:flex-row justify-center'>
|
||||||
<p className='font-serif text-lg basis-full md:basis-1/2 grow mr-1 text-balance'>
|
<p className='font-serif text-lg basis-full md:basis-1/2 grow mr-1 text-balance'>
|
||||||
|
@ -101,7 +108,7 @@ export default function Home() {
|
||||||
<div className='font-serif text-xl'>
|
<div className='font-serif text-xl'>
|
||||||
Our esteemed faculty and alumni (ranked by research output)
|
Our esteemed faculty and alumni (ranked by research output)
|
||||||
</div>
|
</div>
|
||||||
<AuthorDisplay />
|
<AuthorDisplay authors={authors} affiliations={affiliations} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useState } from 'react'
|
import { useMemo } from 'react'
|
||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||||
|
|
||||||
export default function Providers({ children }) {
|
type ProvidersProps = {
|
||||||
const [queryClient] = useState(
|
children: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Providers({ children }: Readonly<ProvidersProps>) {
|
||||||
|
const queryClient = useMemo(
|
||||||
() =>
|
() =>
|
||||||
new QueryClient({
|
new QueryClient({
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
|
@ -12,7 +16,8 @@ export default function Providers({ children }) {
|
||||||
staleTime: 60 * 1000,
|
staleTime: 60 * 1000,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
}),
|
||||||
|
[]
|
||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
@ -32,7 +32,7 @@ const SearchResult = ({
|
||||||
this click handling logic simply checks to see if the element clicked is a nested link
|
this click handling logic simply checks to see if the element clicked is a nested link
|
||||||
and fires a redirect if not, to avoid overriding links in the children
|
and fires a redirect if not, to avoid overriding links in the children
|
||||||
*/
|
*/
|
||||||
const handleClick = (event: React.MouseEvent<HTMLDivElement>) => {
|
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
let target = event.target as HTMLElement
|
let target = event.target as HTMLElement
|
||||||
while (target != null) {
|
while (target != null) {
|
||||||
if (target.nodeName === 'A') {
|
if (target.nodeName === 'A') {
|
||||||
|
@ -44,10 +44,10 @@ const SearchResult = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<button
|
||||||
className={`${cardEffects['card-large']} border-4 rounded-lg border-gray-300 hover:border-blue-500 p-5 my-4 w-full cursor-pointer`}
|
className={`${cardEffects['card-large']} border-4 rounded-lg border-gray-300 hover:border-blue-500 p-5 my-4 w-full cursor-pointer text-left`}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
role='button' // this is a critical DEI concern as we have marked this element as a button with ARIA role, yet we have not supported button accessiblity features
|
type='button'
|
||||||
>
|
>
|
||||||
<h2 className={`${zillaSlab.className} text-3xl`}>{title}</h2>
|
<h2 className={`${zillaSlab.className} text-3xl`}>{title}</h2>
|
||||||
<p className='text-slate-500 py-2 text-md mt-2'>
|
<p className='text-slate-500 py-2 text-md mt-2'>
|
||||||
|
@ -66,7 +66,7 @@ const SearchResult = ({
|
||||||
<p className='py-2 text-md text-slate-700 font-serif text-lg text-balance'>
|
<p className='py-2 text-md text-slate-700 font-serif text-lg text-balance'>
|
||||||
{abstract.substring(0, 500) + (abstract.length > 500 ? '...' : '')}
|
{abstract.substring(0, 500) + (abstract.length > 500 ? '...' : '')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
.card-large {
|
.card-large {
|
||||||
@apply shadow-sm shadow-slate-300;
|
@apply shadow-sm shadow-slate-300;
|
||||||
/* box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); */
|
|
||||||
transition: all 0.6s cubic-bezier(0.165, 0.84, 0.44, 1);
|
transition: all 0.6s cubic-bezier(0.165, 0.84, 0.44, 1);
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
@ -29,7 +28,6 @@
|
||||||
|
|
||||||
.card-small {
|
.card-small {
|
||||||
@apply shadow-sm shadow-slate-300;
|
@apply shadow-sm shadow-slate-300;
|
||||||
/* box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); */
|
|
||||||
transition: all 0.6s cubic-bezier(0.165, 0.84, 0.44, 1);
|
transition: all 0.6s cubic-bezier(0.165, 0.84, 0.44, 1);
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,30 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { notFound } from 'next/navigation'
|
import { notFound } from 'next/navigation'
|
||||||
|
|
||||||
export default class ErrorBoundary extends React.Component {
|
interface ErrorBoundaryProps {
|
||||||
constructor(props) {
|
children: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ErrorBoundaryState {
|
||||||
|
hasError: boolean
|
||||||
|
error: Error | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class ErrorBoundary extends React.Component<
|
||||||
|
ErrorBoundaryProps,
|
||||||
|
ErrorBoundaryState
|
||||||
|
> {
|
||||||
|
constructor(props: ErrorBoundaryProps) {
|
||||||
super(props)
|
super(props)
|
||||||
this.state = { hasError: false, error: null }
|
this.state = { hasError: false, error: null }
|
||||||
}
|
}
|
||||||
|
|
||||||
static getDerivedStateFromError(error) {
|
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
|
||||||
// Update state so the next render will show the fallback UI.
|
// Update state so the next render will show the fallback UI.
|
||||||
return { hasError: true, error }
|
return { hasError: true, error }
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidCatch(error, errorInfo) {
|
componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {
|
||||||
// You can also log the error to an error reporting service
|
// You can also log the error to an error reporting service
|
||||||
console.error('Error caught by Error Boundary:', error, errorInfo)
|
console.error('Error caught by Error Boundary:', error, errorInfo)
|
||||||
}
|
}
|
||||||
|
@ -20,7 +32,7 @@ export default class ErrorBoundary extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
if (this.state.hasError) {
|
if (this.state.hasError) {
|
||||||
// You can render any custom fallback UI
|
// You can render any custom fallback UI
|
||||||
if (this.state.error.message === '404') notFound()
|
if (this.state.error?.message === '404') notFound()
|
||||||
return <h1>Something went wrong.</h1>
|
return <h1>Something went wrong.</h1>
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,12 +30,7 @@ export default function searchDocs(
|
||||||
|
|
||||||
worker.postMessage(query)
|
worker.postMessage(query)
|
||||||
} else {
|
} else {
|
||||||
reject(
|
return
|
||||||
new Error(
|
|
||||||
`Web Workers are not supported in this environment. Please avoid using a prehistoric browser.
|
|
||||||
If nothing else seems wrong, this error message is probably showing up due to ghosts in your browser.`
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// Youwen brainrot
|
||||||
export default function string2hex(str: string): string {
|
export default function string2hex(str: string): string {
|
||||||
// Hash function to convert string to a number
|
// Hash function to convert string to a number
|
||||||
let hash = 0
|
let hash = 0
|
||||||
|
|