Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/cornell-dti/carriage-web
Browse files Browse the repository at this point in the history
…into rj353/location_modal
  • Loading branch information
raissaji committed Oct 30, 2024
2 parents ed92a24 + 11b5e40 commit a0b61f0
Show file tree
Hide file tree
Showing 27 changed files with 1,770 additions and 391 deletions.
496 changes: 489 additions & 7 deletions frontend/package-lock.json

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
"description": "",
"main": "index.js",
"dependencies": {
"@emotion/react": "^11.13.3",
"@emotion/styled": "^11.13.0",
"@mui/icons-material": "^6.1.5",
"@mui/material": "^6.1.5",
"@react-aria/utils": "^3.25.2",
"@react-oauth/google": "^0.12.1",
"@types/crypto-js": "^4.2.2",
Expand Down Expand Up @@ -33,6 +37,7 @@
"react-router-dom": "^6.26.2",
"react-router-hash-link": "^2.4.3",
"react-scripts": "^5.0.1",
"react-select": "^5.8.1",
"reactjs-popup": "^2.0.6"
},
"scripts": {
Expand Down Expand Up @@ -71,4 +76,4 @@
"eslint-plugin-react-hooks": "^4.6.0",
"prettier": "2.8.8"
}
}
}
2 changes: 1 addition & 1 deletion frontend/src/components/Card/card.module.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.card {
display: inline-block;
width: 17.2rem;
height: 23rem;
/* height: 23rem; */
background-color: white;
border-radius: 0.5rem;
overflow: hidden;
Expand Down
77 changes: 39 additions & 38 deletions frontend/src/components/EmployeeCards/EmployeeCards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import { useState, useMemo } from 'react';
import { Link } from 'react-router-dom';
import Card, { CardInfo } from '../Card/Card';
import styles from './employeecards.module.css';
import { clock, phone, wheel, user } from '../../icons/userInfo/index';
import { phone, wheel, user } from '../../icons/userInfo/index'; // clock,
import { Employee } from '../../types';
import { useEmployees } from '../../context/EmployeesContext';
import { AdminType } from '../../../../server/src/models/admin';
import { DriverType } from '../../../../server/src/models/driver';
import { Button } from '../FormElements/FormElements';
import Pagination from '@mui/material/Pagination';

const formatPhone = (phoneNumber: string) => {
const areaCode = phoneNumber.substring(0, 3);
Expand Down Expand Up @@ -65,32 +66,6 @@ const EmployeeCard = ({
}: EmployeeCardProps) => {
const netId = email.split('@')[0];
const fmtPhone = formatPhone(phoneNumber);

/**
* Formats availability, represented by an object that maps available days to
* start and end times, into a printable string with availabilities formatted as '[day]: [start] - [end]'.
* Ignores malformed availabilities, e.g. missing start or end times, from being printed.
*
* @param availability the driver's availability, represented as an object map of days to start and end times
* @returns a string representation of a driver's availibility
*/
const formatAvail = (availability: {
[key: string]: { startTime: string; endTime: string };
}) => {
if (!availability) {
return 'N/A';
}

return Object.entries(availability)
.filter(([_, timeRange]) => timeRange?.startTime && timeRange?.endTime)
.map(
([day, timeRange]) =>
`${day}: ${timeRange.startTime} - ${timeRange.endTime}`
)
.join('\n ');
};

const parsedAvail = formatAvail(availability!);
const isAdmin = isDriver !== undefined;
const isBoth = isDriver && isDriver == true;

Check warning on line 70 in frontend/src/components/EmployeeCards/EmployeeCards.tsx

View workflow job for this annotation

GitHub Actions / check

Expected '===' and instead saw '=='
const roles = (): string => {
Expand All @@ -106,7 +81,7 @@ const EmployeeCard = ({
netId,
type,
phone: fmtPhone,
availability: parsedAvail,
// availability: parsedAvail,
photoLink,
startDate,
};
Expand All @@ -130,15 +105,6 @@ const EmployeeCard = ({
<CardInfo icon={phone} alt="phone">
<p>{fmtPhone}</p>
</CardInfo>

<CardInfo icon={clock} alt="clock">
{parsedAvail ? (
<p className={styles.timeText}>{parsedAvail}</p>
) : (
<p>N/A</p>
)}
</CardInfo>

<CardInfo
icon={isAdmin || isBoth ? user : wheel}
alt={isAdmin || isBoth ? 'admin' : 'wheel'}
Expand All @@ -150,6 +116,13 @@ const EmployeeCard = ({
);
};

/* <CardInfo icon={clock} alt="clock">
{parsedAvail ? (
<p className={styles.timeText}>{parsedAvail}</p>
) : (
<p>N/A</p>
)}
</CardInfo> */
const searchableFields = (employee: DriverType | AdminType) => {
const fields = [
employee.firstName,
Expand Down Expand Up @@ -183,6 +156,10 @@ const EmployeeCards = ({ query }: EmployeeCardsProps) => {
const [filterAdmin, setFilterAdmin] = useState(false);
const [filterDriver, setFilterDriver] = useState(false);

// Pagination state
const [page, setPage] = useState(1);
const [pageSize] = useState(8);

const employees = useMemo(() => {
const allEmployees = [...admins, ...drivers];
const employeeSet: Record<string, DriverType | AdminType> = {};
Expand Down Expand Up @@ -210,6 +187,18 @@ const EmployeeCards = ({ query }: EmployeeCardsProps) => {
return sortedEmployees.filter(matchesQuery(query));
}, [admins, drivers, query, filterAdmin, filterDriver]);

// Calculate total pages and get the employees on given page
const totalPages = Math.ceil(employees.length / pageSize);
const paginatedEmployees = employees.slice(
(page - 1) * pageSize,
page * pageSize
);
const handlePageChange = (
event: React.ChangeEvent<unknown>,
value: number
) => {
setPage(value);
};
return (
<>
<div className={styles.filtersContainer}>
Expand All @@ -229,14 +218,26 @@ const EmployeeCards = ({ query }: EmployeeCardsProps) => {
</Button>
</div>
<div className={styles.cardsContainer}>
{employees.map((employee) => (
{paginatedEmployees.map((employee) => (
<EmployeeCard
key={employee.id}
id={employee.id}
employee={employee}
/>
))}
</div>
{totalPages > 1 && (
<div className={styles.paginationContainer}>
<Pagination
count={totalPages}
page={page}
onChange={handlePageChange}
size="large"
showFirstButton
showLastButton
/>
</div>
)}
</>
);
};
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/components/EmployeeCards/employeecards.module.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
.cardsContainer {
display: flex;
flex-wrap: wrap;
height: 38rem; /* Fix the cards to a certain height so that pagination component stays fixed */
margin-top: 1rem;
}

Expand All @@ -21,3 +24,10 @@
display: flex;
gap: 1rem;
}

.paginationContainer {
display: flex;
justify-content: center;
margin: 2rem 0;
padding: 1rem;
}
66 changes: 66 additions & 0 deletions frontend/src/components/FormElements/FormElements.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import React, { SelectHTMLAttributes } from 'react';

Check warning on line 1 in frontend/src/components/FormElements/FormElements.tsx

View workflow job for this annotation

GitHub Actions / check

'SelectHTMLAttributes' is defined but never used
import cn from 'classnames';
import styles from './formelements.module.css';
import Select, { ActionMeta, Props as SelectProps } from 'react-select';
import {
Control,
RegisterOptions,
useController,
Path,
FieldValues,
} from 'react-hook-form';

type LabelType = React.DetailedHTMLProps<
React.LabelHTMLAttributes<HTMLLabelElement>,
Expand Down Expand Up @@ -73,3 +81,61 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
);
}
);

type Option = {
id: string;
name: string;
};

type SelectOption = {
value: string;
label: string;
};

type SelectComponentProps<TFieldValues extends FieldValues> = SelectProps & {
control: Control<TFieldValues>;
name: Path<TFieldValues>;
datalist: Option[];
className?: string;
rules?: RegisterOptions<TFieldValues>;
};

export const SelectComponent = <TFieldValues extends FieldValues>({
control,
name,
datalist,
className,
rules,
...rest
}: SelectComponentProps<TFieldValues>) => {
const {
field: { onChange, value, ref, ...inputProps },
} = useController<TFieldValues>({
name,
control,
rules,
});

const transformedOptions = datalist.map((data) => ({
value: data.id,
label: data.name,
}));

const selectedOption = transformedOptions.find(
(option) => option.value === value
);

return (
<Select
{...inputProps}
options={transformedOptions}
className={cn(styles.customSelect, className)}
onChange={(newValue: unknown) => {
const option = newValue as SelectOption | null;
onChange(option?.value);
}}
value={selectedOption}
{...rest}
/>
);
};
Loading

0 comments on commit a0b61f0

Please sign in to comment.