chore: fix all the errors

Sonarlint, ESLint, you name it; it's fixed

AI summary of some changes:
feat: update image handling and optimization in Next.js

The changes in this commit update the handling and optimization of images in Next.js. 

- The `Image` component from the `next/image` package is now used instead of the regular `img` tag. This allows for better handling and optimization of images in Next.js applications.
- The `unoptimized` property in the `nextConfig` object is set to `false`, which means that images will be optimized by default. This improves the performance and loading speed of the application.
- The `remotePatterns` property in the `nextConfig` object is added to specify remote patterns for image optimization. This allows for more control over which images should be optimized and which should not.

These changes were made to enhance the performance and user experience of the application by optimizing the handling of images.
This commit is contained in:
q9i 2024-02-17 14:54:14 -08:00 committed by GitButler
parent 9cf06fcdc2
commit 0443604d04
30 changed files with 287 additions and 252 deletions

View file

@ -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"
} }
} }

View file

@ -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: '/**',
},
],
}, },
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 921 KiB

After

Width:  |  Height:  |  Size: 907 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 MiB

After

Width:  |  Height:  |  Size: 6.9 MiB

View file

@ -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,21 @@ 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 +39,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 +62,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 && (
<> <>

View file

@ -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}
/> />

View file

@ -1,32 +1,24 @@
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'] })
export default function AuthorDisplay({ const MainAuthorPosition = ({
author, author
}: Readonly<{ author: string }>) { }: { author: Author }) => {
const data = authors[author] const { name, affiliation, website } = author
if (!data) {
notFound()
}
const { name, affiliation, image, nationality, formerAffiliations } = data
const authorsDocuments = findDocumentsByAuthor(author)
const MainPosition = () => {
const mainAffiliationShort = affiliation[0].split('@')[1] const mainAffiliationShort = affiliation[0].split('@')[1]
const mainPosition = affiliation[0].split('@')[0] const mainPosition = affiliation[0].split('@')[0]
const mainAffiliation = affiliations[mainAffiliationShort] const mainAffiliation = affiliations[mainAffiliationShort]
const { website } = data
return ( return (
<> <>
@ -49,10 +41,12 @@ export default function AuthorDisplay({
href={`/affiliation/${a.split('@')[1]}`} href={`/affiliation/${a.split('@')[1]}`}
className={`${cardEffects['card-small']} rounded-md`} className={`${cardEffects['card-small']} rounded-md`}
> >
<img <Image
src={affiliations[a.split('@')[1]].image} src={affiliations[a.split('@')[1]].image}
alt={affiliations[a.split('@')[1]].name} alt={affiliations[a.split('@')[1]].name}
className='h-16 rounded-md p-2' width={100}
height={100}
className='h-16 rounded-md p-2 object-contain'
/> />
</Link> </Link>
))} ))}
@ -61,7 +55,11 @@ export default function AuthorDisplay({
) )
} }
const OtherPositions = () => { const OtherAuthorPositions = ({
author
}: { author: Author }) => {
const { affiliation } = author
if (affiliation.length === 1) return if (affiliation.length === 1) return
return ( return (
<> <>
@ -86,7 +84,11 @@ export default function AuthorDisplay({
) )
} }
const FormerPositions = () => { const FormerAuthorPositions = ({
author
}: { author: Author }) => {
const { formerAffiliations } = author
if (!formerAffiliations) return null if (!formerAffiliations) return null
return ( return (
<> <>
@ -112,8 +114,10 @@ export default function AuthorDisplay({
) )
} }
const Bio = () => { const AuthorBio = ({
const { bio } = data author
}: { author: Author }) => {
const { bio } = author
if (!bio) return null if (!bio) return null
return ( return (
@ -124,6 +128,18 @@ export default function AuthorDisplay({
) )
} }
export default function AuthorDisplay({
author,
}: Readonly<{ author: string }>) {
const data = authors[author]
if (!data) {
notFound()
}
const { name, image, nationality } = data
const authorsDocuments = findDocumentsByAuthor(author)
return ( return (
<> <>
<Suspense> <Suspense>
@ -131,9 +147,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 +164,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' />

View file

@ -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} />

View file

@ -93,11 +93,6 @@ export const Authors = ({
) )
} }
export const Reviewers = ({
reviewers,
showTitle = true,
}: Readonly<{ reviewers: reviewer[] | undefined; showTitle?: boolean }>) => {
if (!reviewers) return null
const ReviewerDisplay = ({ r }: Readonly<{ r: reviewer }>) => { const ReviewerDisplay = ({ r }: Readonly<{ r: reviewer }>) => {
if (r.profile) { if (r.profile) {
return ( return (
@ -120,6 +115,12 @@ export const Reviewers = ({
) )
} }
export const Reviewers = ({
reviewers,
showTitle = true,
}: Readonly<{ reviewers: reviewer[] | undefined; showTitle?: boolean }>) => {
if (!reviewers) return null
return ( return (
<> <>
{showTitle ? <span className='font-bold'>Reviewed by: </span> : null} {showTitle ? <span className='font-bold'>Reviewed by: </span> : null}

View file

@ -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'

View file

@ -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>
) )
} }

View file

@ -55,7 +55,7 @@ export default function MobileMenu() {
> >
open-source contributors open-source contributors
</a> </a>
. {'.'}
</p> </p>
</div> </div>
</> </>

View file

@ -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.
* *
@ -9,7 +8,7 @@ export default function Container({
children, children,
}: Readonly<{ children: React.ReactNode }>) { }: Readonly<{ children: React.ReactNode }>) {
return ( return (
<div className={`${styles.container} pb-10 px-5 max-w-[1200px] mx-auto`}> <div className={`pb-10 px-5 max-w-[1200px] mx-auto`}>
{children} {children}
</div> </div>
) )

View file

@ -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;
}
} */

View file

@ -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]

View file

@ -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)

View file

@ -112,7 +112,7 @@ const VersionChooser = ({
)} )}
</select> </select>
</div> </div>
) );
} }
export default VersionChooser export default VersionChooser

View file

@ -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>

View file

@ -1,5 +1,5 @@
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 +35,17 @@ 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 }
}
const AuthorDisplay = ({ authors, affiliations }: AuthorDisplayProps) => {
return ( return (
<ol className='list-decimal pl-4 space-y-2'> <ol className='list-decimal pl-4 space-y-2'>
{sortAuthorsByDocumentsPublished(authors).map(({ id, author }) => { {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}`}>
@ -66,6 +64,7 @@ export default function Home() {
) )
} }
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 +100,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>
) )

View file

@ -1,19 +1,21 @@
'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: {
queries: { queries: {
staleTime: 60 * 1000, staleTime: 60 * 1000,
}, },
}, },
}) }), [])
)
return ( return (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider> <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
) )

View file

@ -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>
) )
} }

View file

@ -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;
} }

View file

@ -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>
} }

View file

@ -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.`
)
)
} }
}) })
} }

View file

@ -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