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.
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"extends": "next/core-web-vitals",
|
||||
"rules": {
|
||||
"@next/next/no-img-element": "off"
|
||||
"@next/next/no-img-element": "error"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
images: {
|
||||
unoptimized: true,
|
||||
unoptimized: false,
|
||||
remotePatterns: [
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: 'upload.wikimedia.org',
|
||||
port: '',
|
||||
pathname: '/**',
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
|
|
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 { 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 && (
|
||||
<>
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
|
|
|
@ -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' />
|
||||
|
|
|
@ -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} />
|
||||
|
|
|
@ -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 (
|
||||
<>
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ export default function MobileMenu() {
|
|||
>
|
||||
open-source contributors
|
||||
</a>
|
||||
.
|
||||
{'.'}
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
|
|
|
@ -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',
|
||||
'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]
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -112,7 +112,7 @@ const VersionChooser = ({
|
|||
)}
|
||||
</select>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default VersionChooser
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
|
|
|
@ -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>
|
||||
)
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// Youwen brainrot
|
||||
export default function string2hex(str: string): string {
|
||||
// Hash function to convert string to a number
|
||||
let hash = 0
|
||||
|
|