Skip to content
Open
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
55 changes: 55 additions & 0 deletions apps/frontend/src/app/components/AccountsPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use client';

import React from 'react';
import StaffCard from './StaffCard';

interface User {
user_id: number;
name: string;
email: string;
is_admin: boolean;
created_at?: string;
}

const mockUsers: User[] = [
{ user_id: 1, name: 'Mehana Nagarur', email: '[email protected]', is_admin: true },
{ user_id: 2, name: 'Alex Rivera', email: '[email protected]', is_admin: true },
{ user_id: 3, name: 'Jordan Lee', email: '[email protected]', is_admin: true },
{ user_id: 4, name: 'Priya Sharma', email: '[email protected]', is_admin: true },
{ user_id: 5, name: 'Chris Nguyen', email: '[email protected]', is_admin: true },
{ user_id: 6, name: 'Taylor Brooks', email: '[email protected]', is_admin: false },
{ user_id: 7, name: 'Sam Patel', email: '[email protected]', is_admin: false },
{ user_id: 8, name: 'Morgan Clarke', email: '[email protected]', is_admin: false },
{ user_id: 9, name: 'Jamie Wu', email: '[email protected]', is_admin: false },
{ user_id: 10, name: 'Riley Thompson', email: '[email protected]',is_admin: false },
{ user_id: 11, name: 'Avery Johnson', email: '[email protected]', is_admin: false },
{ user_id: 12, name: 'Casey Martinez', email: '[email protected]',is_admin: false },
{ user_id: 13, name: 'Drew Hassan', email: '[email protected]', is_admin: false },
{ user_id: 14, name: 'Quinn Okafor', email: '[email protected]', is_admin: false },
{ user_id: 15, name: 'Blake Fernandez', email: '[email protected]',is_admin: false },
];

export const facilitationTeam = mockUsers.filter(u => u.is_admin);
export const teamMembers = mockUsers.filter(u => !u.is_admin);



export default function AccountsPage() {
return (
<div>
<h1 className="![font-family:var(--font-heading)] !text-[length:var(--font-size-heading-1)] !font-semibold">Accounts</h1>
<h3 className="![font-family:var(--font-heading)] !text-[length:var(--font-size-heading-3)] !font-semibold">Core BRANCH Facilitation Team</h3>
<div className="grid grid-cols-3 md:grid-cols-7 gap-3 !pt-3 !pb-7">
{facilitationTeam.map(user => (
<StaffCard key={user.user_id} name={user.name} />
))}
</div>
<h3 className="![font-family:var(--font-heading)] !text-[length:var(--font-size-heading-3)] !font-semibold">BRANCH Team Members</h3>
<div className="grid grid-cols-3 md:grid-cols-7 gap-3 !pt-3 !pb-7">
{teamMembers.map(user => (
<StaffCard key={user.user_id} name={user.name} />
))}
</div>
</div>
);
}
24 changes: 12 additions & 12 deletions apps/frontend/src/app/components/StaffCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,33 @@
import React from 'react';
import Image from 'next/image';
import { useState } from 'react';
import { PiUserCircleThin } from "react-icons/pi";


interface StaffCardProps {
image?: string;
name: string;
title: string;
}


export default function StaffCard({
image,
name,
title
name
}: StaffCardProps) {
const [imgError, setImgError] = useState(false);

return (
<div className="!w-[90px] h-100 flex flex-col items-center !pt-3 !gap-1 overflow-hidden">
{(image && !imgError) ? (
<Image src={image} alt="Staff" width={90} height={105} className="object-cover ![aspect-ratio:44/51] w-full" onError={() => setImgError(true)}/>
) : (
<div className="!w-[90px] ![aspect-ratio:44/51] bg-accent-dark-green"></div>
)}
<div className="flex flex-col items-center gap-1 !w-[90px]">
<p className="![font-family:var(--font-body)] !text-[length:var(--font-size-body)] !font-normal break-words w-full text-center">{name}</p>
<p className="![font-family:var(--font-family-body)] !text-[length:var(--font-size-callout)] !font-bold break-words w-full text-center">{title}</p>
<div data-testid="staff-card" className="relative !w-full !flex flex-col items-center !p-3 !gap-1 overflow-hidden !border-1 !border-[var(--color-black-300)] !border-solid">
<div className="w-full aspect-square">
{(image && !imgError) ? (
<Image src={image} alt="Staff" width={120} height={120} className="object-cover w-full h-full" onError={() => setImgError(true)}/>
) : (
<div data-testid="staff-placeholder" className="w-full h-full flex items-center justify-center bg-[var(--color-primary-300)]">
<PiUserCircleThin size="100%" className="text-[var(--color-accent-dark-green)]" />
</div>
)}
</div>
<p className="![font-family:var(--font-body)] !text-[length:var(--font-size-callout)] !font-bold break-words w-full text-center !pt-1 !pb-2">{name}</p>
</div>
)
}
35 changes: 35 additions & 0 deletions apps/frontend/test/components/AccountsPage.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { render, screen } from '../utils';
import AccountsPage, { facilitationTeam, teamMembers } from '@/app/components/AccountsPage';

describe('AccountsPage', () => {
it('renders the headings', () => {
render(<AccountsPage />);
expect(screen.getByText('Accounts')).toBeInTheDocument();
expect(screen.getByText('Core BRANCH Facilitation Team')).toBeInTheDocument();
expect(screen.getByText('BRANCH Team Members')).toBeInTheDocument();
});

it('renders the correct staff cards in the facilitation section', () => {
render(<AccountsPage />);
const section = screen.getByText('Core BRANCH Facilitation Team').closest('div');
const cards = section?.querySelectorAll('[data-testid="staff-card"]');

if (facilitationTeam.length === 0) {
expect(cards?.length).toBe(0);
} else {
expect(cards?.length).toBeGreaterThan(0);
}
});

it('renders the correct staff cards in the team members section', () => {
render(<AccountsPage />);
const section = screen.getByText('BRANCH Team Members').closest('div');
const cards = section?.querySelectorAll('[data-testid="staff-card"]');

if (teamMembers.length === 0) {
expect(cards?.length).toBe(0);
} else {
expect(cards?.length).toBeGreaterThan(0);
}
});
});
26 changes: 10 additions & 16 deletions apps/frontend/test/components/StaffCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,31 @@ import StaffCard from '@/app/components/StaffCard';

describe ('StaffCard', () => {
it('renders the placeholder image when no image given', () => {
render(<StaffCard name="name" title="title" />);
expect(document.querySelector('.bg-accent-dark-green')).toBeInTheDocument();
render(<StaffCard name="name" />);
expect(document.querySelector('[data-testid="staff-placeholder"]')).toBeInTheDocument();
});

it('renders the placeholder image when image given image has error', () => {
render(<StaffCard image="/" name="name" title="title" />);
render(<StaffCard image="/" name="name" />);
const img = document.querySelector('img');
fireEvent.error(img!);
expect(document.querySelector('.bg-accent-dark-green')).toBeInTheDocument();
expect(document.querySelector('[data-testid="staff-placeholder"]')).toBeInTheDocument();
});

it('renders the given image', () => {
render(<StaffCard image="/test.jpg" name="name" title="title" />);
render(<StaffCard image="/test.jpg" name="name" />);
const img = document.querySelector('img');
expect(img).toHaveAttribute('src', expect.stringContaining('test.jpg'));
});

it('renders the name', () => {
render(<StaffCard name="name" title="title" />);
render(<StaffCard name="name" />);
expect(screen.getByText('name')).toBeInTheDocument();
});

it('renders the title', () => {
render(<StaffCard name="name" title="title" />);
expect(screen.getByText('title')).toBeInTheDocument();
});

it('long name and title are wrapped', () => {
render(<StaffCard name="superduper longname" title="superduper longtitle" />);
it('long name is wrapped', () => {
render(<StaffCard name="superduper longname" />);
const name = screen.getByText('superduper longname');
const title = screen.getByText('superduper longtitle');
expect(name).toHaveClass('break-words');
expect(title).toHaveClass('break-words'); });
expect(name).toHaveClass('break-words');
});
})
Loading