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",
"rules": {
"@next/next/no-img-element": "off"
"@next/next/no-img-element": "error"
}
}

View file

@ -1,7 +1,15 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
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 { Fragment } from 'react'
import DocumentCard from '@/app/components/DocumentCard'
import Image from 'next/image'
const zillaSlab = Zilla_Slab({ subsets: ['latin'], weight: ['500'] })
@ -12,6 +13,21 @@ export function generateStaticParams() {
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({
params,
}: Readonly<{ params: { shortName: string } }>) {
@ -23,25 +39,14 @@ export default function Page({
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 (
<div>
<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'>
<img
<Image
alt='profile'
width={1000}
height={1000}
className='rounded-lg mx-auto p-8 object-cover w-full h-full'
src={image}
/>
@ -57,7 +62,7 @@ export default function Page({
<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' />
<br />
<Description />
<Description description={description} />
</div>
{affiliationDocuments.length > 0 && (
<>

View file

@ -3,6 +3,7 @@ import { notFound } from 'next/navigation'
import { Zilla_Slab } from 'next/font/google'
import getAffiliations from './getAffiliations'
import { Fragment } from 'react'
import Image from 'next/image'
const zillaSlab = Zilla_Slab({ subsets: ['latin'], weight: ['500'] })
@ -24,8 +25,10 @@ const AffiliationCard = ({
<div className='m-4'>
<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'>
<img
<Image
alt='profile'
width={1000}
height={1000}
className='rounded-lg mx-auto p-8 object-cover w-full h-full'
src={image}
/>

View file

@ -1,15 +1,133 @@
import Link from 'next/link'
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 { notFound } from 'next/navigation'
import DocumentCard from '@/app/components/DocumentCard'
import findDocumentsByAuthor from './findDocumentsByAuthor'
import cardEffects from '@/app/styles/cardEffects.module.css'
import KonamiSnowfall from './KonamiSnowfall'
import Image from 'next/image'
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({
author,
}: Readonly<{ author: string }>) {
@ -18,112 +136,10 @@ export default function AuthorDisplay({
notFound()
}
const { name, affiliation, image, nationality, formerAffiliations } = data
const { name, image, nationality } = data
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 (
<>
<Suspense>
@ -131,9 +147,11 @@ export default function AuthorDisplay({
</Suspense>
<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'>
<img
<Image
alt='profile'
className='rounded-full mx-auto object-cover w-full h-full border-slate-800 border-4'
width={500}
height={500}
src={image}
/>
</div>
@ -146,16 +164,16 @@ export default function AuthorDisplay({
{name.nickname ? ` "${name.nickname}"` : null} {name.last}
</span>
<div className='text-slate-600 text-md sm:text-lg mt-4'>
<MainPosition />
<MainAuthorPosition author={data} />
</div>
</div>
</div>
</div>
<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' />
<OtherPositions />
<FormerPositions />
<Bio />
<OtherAuthorPositions author={data} />
<FormerAuthorPositions author={data} />
<AuthorBio author={data} />
{authorsDocuments.length > 0 && (
<>
<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'
import Konami from 'react-konami-code'
import { Snowfall } from 'react-snowfall'
import { useEffect, useState } from 'react'
import { Fragment, useEffect, useState } from 'react'
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({
nationalityList,
@ -28,23 +47,6 @@ export default function KonamiSnowfall({
setImages(imagesTemp)
}, [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 (
<>
<Konami action={handleKonami} />

View file

@ -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 = ({
reviewers,
showTitle = true,
}: Readonly<{ reviewers: reviewer[] | undefined; showTitle?: boolean }>) => {
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 (
<>

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
import { loadAllTopics, loadAllAuthors } from '@/app/db/loaders'
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
return (
<Link href={href} className='no-link-style'>
<div
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`}
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
<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 text-left`}
>
<h2 className={`${zillaSlab.className} text-3xl`}>{title}</h2>
<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'>
{abstract.substring(0, 500) + (abstract.length > 500 ? '...' : '')}
</p>
</div>
</button>
</Link>
)
}

View file

@ -55,7 +55,7 @@ export default function MobileMenu() {
>
open-source contributors
</a>
.
{'.'}
</p>
</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.
*
@ -9,7 +8,7 @@ export default function Container({
children,
}: Readonly<{ children: React.ReactNode }>) {
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}
</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',
'distance based cost',
'computer vision foundation',
'study of cost smoothnes',
'study of cost smoothness',
'adversarial attacks',
],
latest: 1,
@ -416,7 +416,7 @@ export const topics: Readonly<{ [key: string]: Topic }> = {
eecs: {
name: 'Electrical Engineering and Computer Science',
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',
},
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
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
in the affiliations data
@ -543,7 +543,7 @@ export const authors: Readonly<{ [key: string]: Author }> = {
},
affiliation: ['Vision Researcher@1280-eecs', 'Student@srvhs'],
image: '/img/profiles/gostler.jpg',
nationality: ['usa'],
nationality: ['fra', 'usa'],
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.`,
},
@ -884,7 +884,7 @@ Raid Zero's influence extends beyond the technical achievements in robotics comp
name: `Team 1280, the Ragin' C-Biscuits`,
short: 'Team 1280',
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]
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]

View file

@ -23,7 +23,7 @@ const checkIsStringArray = (data: unknown): data is string[] => {
onmessage = (e) => {
let authorIds: string[] = []
checkIsStringArray(e.data) && (authorIds = e.data as string[])
checkIsStringArray(e.data) && (authorIds = e.data)
let results = getAuthorsById(authorIds)
postMessage(results)

View file

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

View file

@ -9,6 +9,7 @@ import MobileMenu from './components/MobileMenu'
import { ToastContainer } from 'react-toastify'
import 'react-toastify/dist/ReactToastify.css'
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,
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='max-w-[1200px] flex flex-nowrap mx-auto justify-between items-center'>
<Link href='/affiliation/1280-eecs'>
<img
className='h-[100px] mt-4'
<Image
className='mt-4'
layout='intrinsic'
width={244}
height={100}
src='/img/logos/eecs-wordmark.png'
alt='EECS'
/>
@ -52,7 +56,7 @@ export default function RootLayout({
>
open-source contributors
</a>
.
{'.'}
</p>
</div>
</div>

View file

@ -1,5 +1,5 @@
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 RandomDocs from './components/RandomDocs'
import RecentDocuments from './components/RecentDocuments'
@ -35,37 +35,36 @@ function sortAuthorsByDocumentsPublished(authors: {
return authorsWithId.map(({ id, author }) => ({ id, author }))
}
interface AuthorDisplayProps {
authors: { [key: string]: Author }
affiliations: { [key: string]: Affiliation }
}
const AuthorDisplay = ({ authors, affiliations }: AuthorDisplayProps) => {
return (
<ol className='list-decimal pl-4 space-y-2'>
{sortAuthorsByDocumentsPublished(authors).slice(0, 10).map(({ id, author }) => {
let data = author
let affiliationSlug = data.affiliation[0].split('@')[1]
let affiliation = affiliations[affiliationSlug]
return (
<li key={id}>
<Link href={`/author/${id}`}>
{data.name.first}
{data.name.nickname ? ` "${data.name.nickname}" ` : ' '}
{data.name.last}
</Link>{' '}
of{' '}
<Link href={`/affiliation/${affiliationSlug}`}>
{affiliation.short}
</Link>
</li>
)
})}
</ol>
)
}
export default function Home() {
const AuthorDisplay = () => {
let i = 0
return (
<ol className='list-decimal pl-4 space-y-2'>
{sortAuthorsByDocumentsPublished(authors).map(({ id, author }) => {
let data = author
let affiliationSlug = data.affiliation[0].split('@')[1]
let affiliation = affiliations[affiliationSlug]
i++
if (i > 10) return
return (
<li key={id}>
<Link href={`/author/${id}`}>
{data.name.first}
{data.name.nickname ? ` "${data.name.nickname}" ` : ' '}
{data.name.last}
</Link>{' '}
of{' '}
<Link href={`/affiliation/${affiliationSlug}`}>
{affiliation.short}
</Link>
</li>
)
})}
</ol>
)
}
return (
<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'>
@ -101,7 +100,7 @@ export default function Home() {
<div className='font-serif text-xl'>
Our esteemed faculty and alumni (ranked by research output)
</div>
<AuthorDisplay />
<AuthorDisplay authors={authors} affiliations={affiliations} />
</div>
</div>
)

View file

@ -1,19 +1,21 @@
'use client'
import { useState } from 'react'
import { useMemo } from 'react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
export default function Providers({ children }) {
const [queryClient] = useState(
() =>
type ProvidersProps = {
children: React.ReactNode
}
export default function Providers({ children }: Readonly<ProvidersProps>) {
const queryClient = useMemo(() =>
new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000,
},
},
})
)
}), [])
return (
<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
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
while (target != null) {
if (target.nodeName === 'A') {
@ -44,10 +44,10 @@ const SearchResult = ({
}
return (
<div
className={`${cardEffects['card-large']} border-4 rounded-lg border-gray-300 hover:border-blue-500 p-5 my-4 w-full cursor-pointer`}
<button
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}
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>
<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'>
{abstract.substring(0, 500) + (abstract.length > 500 ? '...' : '')}
</p>
</div>
</button>
)
}

View file

@ -2,7 +2,6 @@
.card-large {
@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);
position: relative;
}
@ -29,7 +28,6 @@
.card-small {
@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);
position: relative;
}

View file

@ -1,18 +1,30 @@
import React from 'react'
import { notFound } from 'next/navigation'
export default class ErrorBoundary extends React.Component {
constructor(props) {
interface ErrorBoundaryProps {
children: React.ReactNode
}
interface ErrorBoundaryState {
hasError: boolean
error: Error | null
}
export default class ErrorBoundary extends React.Component<
ErrorBoundaryProps,
ErrorBoundaryState
> {
constructor(props: ErrorBoundaryProps) {
super(props)
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.
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
console.error('Error caught by Error Boundary:', error, errorInfo)
}
@ -20,7 +32,7 @@ export default class ErrorBoundary extends React.Component {
render() {
if (this.state.hasError) {
// 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>
}

View file

@ -30,12 +30,7 @@ export default function searchDocs(
worker.postMessage(query)
} else {
reject(
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.`
)
)
return
}
})
}

View file

@ -1,3 +1,4 @@
// Youwen brainrot
export default function string2hex(str: string): string {
// Hash function to convert string to a number
let hash = 0