Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 22 additions & 7 deletions app/team/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,28 @@ export default function TeamPage() {

{category.members.map((member) => (
<div key={member.name} className={styles.teamMember}>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={member.imageSrc}
alt={member.name}
width={member.imageWidth ?? undefined}
height={member.imageHeight ?? undefined}
/>
{member.imageSrc ? (
/* eslint-disable-next-line @next/next/no-img-element */
<img
src={member.imageSrc}
alt={member.name}
width={member.imageWidth ?? undefined}
height={member.imageHeight ?? undefined}
/>
) : (
<div
className={styles.avatarPlaceholder}
aria-label={`${member.name} (photo pending)`}
>
{member.name
.split(' ')
.map((part) => part[0])
.filter(Boolean)
.slice(0, 2)
.join('')
.toUpperCase()}
</div>
)}
<div>
<h4>
{member.url ? (
Expand Down
15 changes: 15 additions & 0 deletions app/team/team.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,21 @@
object-fit: cover;
}

.avatarPlaceholder {
flex-shrink: 0;
width: 160px;
height: 160px;
border-radius: 4px;
background: #e6e8eb;
color: #4a5563;
display: flex;
align-items: center;
justify-content: center;
font-size: 48px;
font-weight: 500;
letter-spacing: 1px;
}

.teamMember h4 {
margin: 0 0 2px 0;
}
Expand Down
16 changes: 14 additions & 2 deletions lib/data/team.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ export interface TeamMember {
affiliation: string;
/** Institution website URL */
affiliationUrl?: string;
/** Path to team member image (relative to /public) */
imageSrc: string;
/** Path to team member image (relative to /public). Optional — page renders an initials placeholder when absent. */
imageSrc?: string;
/** Image width in pixels (for consistent layout) */
imageWidth?: number;
/** Image height in pixels (for consistent layout) */
Expand Down Expand Up @@ -204,6 +204,18 @@ export const TEAM_DATA: TeamCategory[] = [
},
],
},
{
title: 'Research Associates',
level: 'h3',
members: [
{
name: 'Vibhav Setlur',
url: 'https://github.com/VibhavSetlur',
role: 'Research Associate',
affiliation: 'Argonne National Laboratory',
},
],
},
{
title: 'Developers',
level: 'h3',
Expand Down
11 changes: 10 additions & 1 deletion tests/unit/api/biochem.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,17 @@ describe('Biochem API Integration Tests', () => {

beforeAll(async () => {
biochemApi = await loadBiochemApi();
// Race the live probe against an internal timeout so the catch path runs
// (marking isApiAvailable = false) before the vitest hookTimeout aborts the
// hook itself. Otherwise CI fails on slow networks even though the suite is
// designed to skip gracefully when the API is unreachable.
try {
const res = await biochemApi.getReactions({ limit: 1 });
const res = await Promise.race([
biochemApi.getReactions({ limit: 1 }),
new Promise<never>((_, reject) =>
setTimeout(() => reject(new Error('Biochem API probe timed out after 7s')), 7000),
),
]);
expect(res.docs).toBeDefined();
} catch (e: unknown) {
console.warn('Biochem API is unavailable, skipping tests:', getErrorMessage(e));
Expand Down
Loading