Skip to content
15 changes: 15 additions & 0 deletions apps/frontend/src/app.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { render } from '@testing-library/react';

import App from './app';

describe('App', () => {
it('should render successfully', () => {
const { baseElement } = render(<App />);
expect(baseElement).toBeTruthy();
});

it('should have a greeting as the title', () => {
const { getByText } = render(<App />);
expect(getByText(/Welcome frontend/gi)).toBeTruthy();
});
});
23 changes: 20 additions & 3 deletions apps/frontend/src/components/ApplicationTable.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import React from 'react';
import { Table } from '@chakra-ui/react';

const COLUMNS = [
Expand Down Expand Up @@ -63,7 +64,23 @@ const APPLICATIONS = [
},
];

export const ApplicationTable: React.FC = () => {
interface ApplicationTableProps {
searchQuery?: string;
}

export function ApplicationTable({ searchQuery = '' }: ApplicationTableProps) {
const filteredApplications = APPLICATIONS.filter((application) => {
if (!searchQuery) return true;
const query = searchQuery.toLowerCase();
return (
application.name.toLowerCase().includes(query) ||
application.discipline.toLowerCase().includes(query) ||
application.disciplineAdminName.toLowerCase().includes(query) ||
application.status.toLowerCase().includes(query) ||
application.experienceType.toLowerCase().includes(query)
);
});

return (
<Table.Root striped stickyHeader>
<Table.Header>
Expand All @@ -80,7 +97,7 @@ export const ApplicationTable: React.FC = () => {
</Table.Row>
</Table.Header>
<Table.Body>
{APPLICATIONS.map((application) => (
{filteredApplications.map((application) => (
<Table.Row key={application.id}>
<Table.Cell>{application.name}</Table.Cell>
<Table.Cell>{application.proposedDate}</Table.Cell>
Expand All @@ -94,6 +111,6 @@ export const ApplicationTable: React.FC = () => {
</Table.Body>
</Table.Root>
);
};
}

export default ApplicationTable;
64 changes: 64 additions & 0 deletions apps/frontend/src/components/ApprovedCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from 'react';
import { Box, Heading, Text, Flex } from '@chakra-ui/react';

interface ApprovedCardProps {
title: string;
count: number;
description: string;
icon: React.ReactNode;
}

export function ApprovedCard({
title,
count,
description,
icon,
}: ApprovedCardProps) {
return (
<Box
borderWidth="2px"
borderColor="black"
borderRadius="16px"
padding="16px"
bg="white"
width="250px"
height="158px"
display="flex"
flexDirection="column"
>
<Flex
justifyContent="space-between"
alignItems="center"
mb="15px"
px="16px"
>
<Heading as="h3" size="md" fontWeight="600" color="#686868">
{title}
</Heading>
<Flex
alignItems="center"
justifyContent="center"
width="24px"
height="24px"
borderRadius="full"
bg="#204AA0"
color="white"
>
{icon}
</Flex>
</Flex>
<Text
fontSize="32px"
fontWeight="600"
mb="15px"
color="#000000"
px="16px"
>
{count}
</Text>
<Text as="h4" fontWeight="600" color="#686868" px="16px">
{description}
</Text>
</Box>
);
}
87 changes: 87 additions & 0 deletions apps/frontend/src/components/PageCounter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
interface PageCounterProps {
page: number;
setPage: (page: number) => void;
maxPages: number;
}

function PageCounter({ page, setPage, maxPages }: PageCounterProps) {
const getPageNumbers = () => {
const pages: (number | string)[] = [];

if (maxPages <= 4) {
// Show all pages if 4 or fewer
for (let i = 1; i <= maxPages; i++) {
pages.push(i);
}
} else {
// Always show first 3 pages when near the start
if (page <= 3) {
pages.push(1, 2, 3);
pages.push('...');
pages.push(maxPages);
}
// Show last 3 pages when near the end
else if (page >= maxPages - 2) {
pages.push(1);
pages.push('...');
pages.push(maxPages - 2, maxPages - 1, maxPages);
}
// Show current page with neighbors in the middle
else {
pages.push(1);
pages.push('...');
pages.push(page - 1, page, page + 1);
pages.push('...');
pages.push(maxPages);
}
}

return pages;
};

return (
<div style={{ display: 'flex', gap: '10px', alignItems: 'center' }}>
{getPageNumbers().map((p, index) =>
typeof p === 'string' ? (
<span
key={`ellipsis-${index}`}
style={{
padding: '8px',
fontFamily: 'Lato, sans-serif',
fontSize: '14px',
fontWeight: 400,
lineHeight: '100%',
color: '#686868',
}}
>
{p}
</span>
) : (
<button
key={p}
onClick={() => setPage(p)}
style={{
padding: '8px',
backgroundColor: '#F8F8F8',
color: '#686868',
border: '1px solid #686868',
borderRadius: '10px',
cursor: 'pointer',
fontFamily: 'Lato, sans-serif',
fontSize: '14px',
fontWeight: 400,
lineHeight: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
{p}
</button>
),
)}
</div>
);
}

export default PageCounter;
41 changes: 41 additions & 0 deletions apps/frontend/src/components/PageTransitionButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { IoChevronBack, IoChevronForward } from 'react-icons/io5';

function PageTransitionButton({
buttonType,
onClick,
}: {
buttonType: 'previous' | 'next';
onClick: () => void;
}) {
return (
<button
onClick={onClick}
style={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
gap: '10px',
padding: '8px',
backgroundColor: '#F8F8F8',
border: '1px solid #686868',
borderRadius: '10px',
cursor: 'pointer',
fontFamily: 'Lato, sans-serif',
fontSize: '14px',
fontWeight: 400,
lineHeight: '100%',
color: '#686868',
}}
>
{buttonType === 'previous' && (
<IoChevronBack style={{ width: '14px', height: '14px' }} />
)}
<span>{buttonType === 'previous' ? 'Previous' : 'Next'}</span>
{buttonType === 'next' && (
<IoChevronForward style={{ width: '14px', height: '14px' }} />
)}
</button>
);
}

export default PageTransitionButton;
52 changes: 52 additions & 0 deletions apps/frontend/src/components/TableSearchBar.tsx
Comment thread
rayyanmridha marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { IoSearch } from 'react-icons/io5';

interface SearchbarProps {
value: string;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
}

function Searchbar({ value, onChange }: SearchbarProps) {
return (
<div
style={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
width: '100%',
padding: '8px 16px',
gap: '10px',
backgroundColor: '#F8F8F8',
border: '1px solid #686868',
borderRadius: '10px',
}}
>
<input
type="text"
placeholder="Search applications by name, email"
value={value}
onChange={onChange}
style={{
flex: 1,
border: 'none',
outline: 'none',
backgroundColor: 'transparent',
fontFamily: 'Lato, sans-serif',
fontSize: '14px',
fontWeight: 400,
lineHeight: '100%',
color: '#686868',
}}
/>
<IoSearch
style={{
width: '24px',
height: '24px',
padding: '3px',
color: '#686868',
}}
/>
</div>
);
}

export default Searchbar;
61 changes: 57 additions & 4 deletions apps/frontend/src/containers/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,31 @@ import clockIcon from '../assets/icons/clock.svg';
import crossIcon from '../assets/icons/cross.svg';
import checkmarkIcon from '../assets/icons/checkmark.svg';
import { Box } from '@chakra-ui/react';
import { useState } from 'react';
import PageTransitionButton from '@components/PageTransitionButton';
import Searchbar from '@components/TableSearchBar';
import PageCounter from '@components/PageCounter';
import ApplicationTable from '@components/ApplicationTable';

const Root: React.FC = () => {
const [searchQuery, setSearchQuery] = useState('');
const [page, setPage] = useState(1);

function onChange(e: React.ChangeEvent<HTMLInputElement>) {
setSearchQuery(e.target.value);
}
return (
<div className="flex flex-row">
<div className="flex flex-row h-screen">
<NavBar logo={'BHCHP'} />
<Box id="main-content" p="10" flex="1">
<Box className="flex flex-row gap-6 pl-4 pt-4" marginBottom="5">
<Box
id="main-content"
p="10"
flex="1"
display="flex"
flexDirection="column"
overflow="hidden"
>
<Box className="flex flex-row gap-6 pl-4 pt-4">
<DashboardCard
className="basis-1/4"
title="Total Applications"
Expand Down Expand Up @@ -46,7 +63,43 @@ const Root: React.FC = () => {
icon={checkmarkIcon}
/>
</Box>
<ApplicationTable />
<div
style={{
marginTop: '20px',
marginBottom: '20px',
fontFamily: 'Lato, sans-serif',
fontSize: '20px',
fontWeight: 700,
lineHeight: '100%',
color: '#000000',
}}
>
<p>Recent Applications</p>
</div>

<Searchbar value={searchQuery} onChange={onChange}></Searchbar>
<Box
style={{
display: 'flex',
flexDirection: 'column',
flex: 1,
marginTop: '20px',
}}
>
<ApplicationTable searchQuery={searchQuery} />
</Box>

<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<PageTransitionButton
buttonType="previous"
onClick={() => setPage(page - 1)}
/>
<PageCounter page={page} setPage={setPage} maxPages={1} />
<PageTransitionButton
buttonType="next"
onClick={() => setPage(page + 1)}
/>
</div>
</Box>
</div>
);
Expand Down
Loading