Skip to content

Commit

Permalink
Merge branch 'develop' into IEEE-259-add-description-box-on-teams-hom…
Browse files Browse the repository at this point in the history
…epage
  • Loading branch information
Mustaballer authored Jan 16, 2024
2 parents 092f172 + 4427206 commit aa3b498
Show file tree
Hide file tree
Showing 21 changed files with 976 additions and 90 deletions.
5 changes: 5 additions & 0 deletions hackathon_site/dashboard/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@material-ui/core": "^4.9.13",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "^4.0.0-alpha.52",
"@mui/material": "^5.13.5",
"@mui/x-data-grid": "^6.8.0",
"@reduxjs/toolkit": "^1.3.6",
"@types/jest": "^26.0.23",
"@types/node": "^15.0.2",
Expand All @@ -14,6 +18,7 @@
"@types/yup": "^0.29.13",
"axios": "^0.21.1",
"connected-react-router": "^6.8.0",
"date-fns": "^2.30.0",
"formik": "^2.1.4",
"history": "^4.10.1",
"lint-staged": "^10.2.2",
Expand Down
5 changes: 5 additions & 0 deletions hackathon_site/dashboard/frontend/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,8 @@ export const patch = <T>(uri: string, data?: any): Promise<AxiosResponse<T>> =>
uri = cleanURI(uri);
return axios.patch(`${SERVER_URL}/${uri}`, data, makeConfig());
};

export const _delete = <T>(uri: string): Promise<AxiosResponse<T>> => {
uri = cleanURI(uri);
return axios.delete(`${SERVER_URL}/${uri}`, makeConfig());
};
29 changes: 29 additions & 0 deletions hackathon_site/dashboard/frontend/src/api/orders.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import SubmittedIcon from "assets/images/icons/statusIcons/unfulfilled-status.svg";
import ReadyForPickupIcon from "assets/images/icons/statusIcons/readyforpickup-status.svg";
import PickedUpIcon from "assets/images/icons/statusIcons/checkout-status.svg";
import CancelledIcon from "assets/images/icons/statusIcons/cancelled-status.svg";
import ReturnedIcon from "assets/images/icons/statusIcons/checkout-status.svg";
import PendingIcon from "assets/images/icons/statusIcons/pending-status.svg";
import InProgressIcon from "assets/images/icons/statusIcons/inprogress-status.svg";

import styles from "components/orders/OrdersTable/OrdersTable.module.scss";

export const statusIconMap: { [key: string]: string } = {
Submitted: SubmittedIcon,
ReadyforPickup: ReadyForPickupIcon,
PickedUp: PickedUpIcon,
Cancelled: CancelledIcon,
Returned: ReturnedIcon,
Pending: PendingIcon,
InProgress: InProgressIcon,
};

export const statusStylesMap: { [key: string]: string } = {
Submitted: styles.SubmittedIcon,
ReadyforPickup: styles.ReadyforPickupIcon,
PickedUp: styles.PickedUpIcon,
Cancelled: styles.CancelledIcon,
Returned: styles.ReturnedIcon,
Pending: styles.PendingIcon,
InProgress: styles.InProgressIcon,
};
4 changes: 3 additions & 1 deletion hackathon_site/dashboard/frontend/src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@ export type OrderStatus =
| "Ready for Pickup"
| "Picked Up"
| "Cancelled"
| "Returned";
| "Returned"
| "Pending"
| "In Progress";
export type PartReturnedHealth = "Healthy" | "Heavily Used" | "Broken" | "Lost";

export type ItemsInOrder = Omit<OrderItem, "order" | "time_occurred">;
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
@import "../../../assets/abstracts/mixins";
@import "../../../assets/abstracts/variables";

.container {
display: flex;
flex-direction: row;
align-content: center;
justify-content: flex-start;

width: 100%;
padding: 4px;
border-radius: 50px;
}

.gridContainer {
background-color: #ffffff;
}

.statusIcon {
width: 12px;
margin-left: 5px;
margin-right: 10px;
}

// for OrdersTable
.SubmittedIcon {
color: #b7941e;
background-color: #ffe899;
}

.ReadyforPickupIcon {
color: #43a047;
background-color: #c1edc1;
}

.PickedUpIcon {
color: #757575;
background-color: #d9d9d9;
}

.CancelledIcon {
color: #b00020;
background-color: #ebbcbc;
}

.ReturnedIcon {
color: #757575;
background-color: #d9d9d9;
}

.PendingIcon {
color: #2b7bbc;
background-color: #c3e1ef;
}

.InProgressIcon {
color: #ffa000;
background-color: #ffe3b4;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import React from "react";
import { fireEvent, render, waitFor } from "testing/utils";
import { mockPendingOrders } from "testing/mockData";
import { OrdersTable } from "./OrdersTable";
import { format, parseISO } from "date-fns"; // to parse date
import { orderQtyValueGetter } from "./OrdersTable";
import { createMemoryHistory } from "history";
import { Router } from "react-router-dom";

describe("Orders Table", () => {
test("renders order table", () => {
const { container } = render(<OrdersTable ordersData={mockPendingOrders} />);
// find the rendered Data Grid
const dataGrid = container.querySelector(".MuiDataGrid-root");
expect(dataGrid).toBeInTheDocument();
});

test("Displays the correct number of rows", () => {
const { container } = render(<OrdersTable ordersData={mockPendingOrders} />);

const rows = container.querySelector(".MuiTablePagination-displayedRows");
const rowsText = rows?.textContent || ""; // get the text content of the element
// returns 1-4 of 4

// regex to extract numbers from the text
const numbersInText = rowsText.split(" "); // get the last number 4
expect(numbersInText ? parseInt(numbersInText[2]) : 0).toEqual(
mockPendingOrders.length
);
});

test("Displays data accurately", () => {
const { container } = render(
<OrdersTable ordersData={[mockPendingOrders[0]]} />
); // select the first row

const rows = container.querySelectorAll("div.MuiDataGrid-row");
rows.forEach((row, rowIndex) => {
// Query for the cells in each row
const cells = row.querySelectorAll("div.MuiDataGrid-cell");

expect(cells.length).toBe(6); // 6 fields are displayed in datagrid

// Access and assert the cell data
expect(cells[0].textContent).toBe(
mockPendingOrders[rowIndex].id.toString()
);
expect(cells[1].textContent).toBe(
format(parseISO(mockPendingOrders[rowIndex].created_at), "MMM d, HH:mm")
);
expect(cells[2].textContent).toBe(mockPendingOrders[rowIndex].team_id);
expect(cells[3].textContent).toBe(mockPendingOrders[rowIndex].team_code);
expect(cells[4].textContent).toBe(
mockPendingOrders[rowIndex].items?.length.toString()
);
expect(cells[5].textContent).toBe(mockPendingOrders[rowIndex].status);
});
});

test("ValueGetter caluclates order quantity correctly", () => {
let params = {
value: [
{
id: 6,
hardware_id: 3,
part_returned_health: null,
},
{
id: 7,
hardware_id: 4,
part_returned_health: null,
},
],
}; // initialize grid value getter params

const result = orderQtyValueGetter(params);
expect(result).toBe(2);
});

test("Handles double row click event", async () => {
const history = createMemoryHistory();

const { container } = render(
<Router history={history}>
<OrdersTable ordersData={mockPendingOrders} />
</Router>
);

const rows = container.querySelectorAll("div.MuiDataGrid-row");

fireEvent.doubleClick(rows[0]);

await waitFor(() => {
// Assert that the URL has changed to the expected path
expect(history.location.pathname).toBe("/teams/IEEE"); // Replace with your expected URL
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import React from "react";
import {
DataGrid,
GridCallbackDetails,
GridColDef,
GridEventListener,
GridRowParams,
MuiEvent,
} from "@mui/x-data-grid";
import { ItemsInOrder, Order } from "api/types";
import { format, parseISO } from "date-fns"; // to parse date
import { statusIconMap, statusStylesMap } from "api/orders";
import styles from "./OrdersTable.module.scss";
import { useHistory } from "react-router-dom";

// magic numbers
const pageSizeOptions = [5, 10, 25]; // items displayed per page
const paginationModel = { pageSize: 25, page: 0 }; // defauly number of rows displayed per page

interface OrdersTableProps {
ordersData: Order[];
}

interface IOrderStateIcon {
status: string;
}

export const orderQtyValueGetter = (params: any) => {
const items = params?.value as ItemsInOrder[] | undefined;
return Array.isArray(items) ? items.length : 0;
};

const OrderStateIcon = ({ status }: IOrderStateIcon) => {
const filterState: string = status.replace(/\s+/g, "");
const styleIcon = statusStylesMap[filterState];
const iconSrc = statusIconMap[filterState];

return (
<div className={styles.container}>
<div className={`${styles.container} ${styleIcon}`}>
<img
src={iconSrc}
className={styles.statusIcon}
alt={`${status} icon`}
/>
{status}
</div>
</div>
);
};

const handleEvent = (
params: GridRowParams, // GridRowParams
event: MuiEvent<React.MouseEvent<HTMLElement>>, // MuiEvent<React.MouseEvent<HTMLElement>>
details: GridCallbackDetails,
navigateCallback: (path: string) => void
) => {
const path = `/teams/${params.row.team_code}`;
navigateCallback(path);
};

const OrdersTable = ({ ordersData }: OrdersTableProps) => {
const history = useHistory();

const handleDoubleRowClick: GridEventListener<"rowClick"> = (
params: GridRowParams,
event: MuiEvent<React.MouseEvent<HTMLElement>>,
details: GridCallbackDetails
) => {
handleEvent(params, event, details, (path) => {
history.push(path); // Call the navigateCallback
});
};

const columns: GridColDef[] = [
{ field: "id", headerName: "ID", width: 25, flex: 1 },
{
field: "created_at",
headerName: "Time Ordered",
flex: 1,
valueFormatter: (params) => {
const time = params.value as string;
const date = parseISO(time);
const formattedTime = format(date, "MMM d, HH:mm");
return formattedTime;
},
},
{ field: "team_id", headerName: "Team ID", flex: 1, minWidth: 100 },
{ field: "team_code", headerName: "Team", flex: 1 },
{
field: "items",
headerName: "Order Qty",
flex: 1,
valueGetter: orderQtyValueGetter,
},
{
field: "status",
headerName: "Status",
minWidth: 250,
renderCell: (params) => <OrderStateIcon status={params.value} />,
},
{ field: "updated_at", headerName: "Updated At" },
{ field: "request", headerName: "Request" },
];

return (
<>
<div style={{ width: "100%", height: "700px" }}>
<DataGrid
className={styles.gridContainer}
rows={ordersData}
columns={columns}
autoPageSize={true} // adjusts page size to fit available area
pageSizeOptions={pageSizeOptions}
columnVisibilityModel={{
// hide specific columns
updated_at: false,
request: false,
}}
initialState={{
pagination: {
paginationModel: paginationModel,
},
}}
onRowDoubleClick={handleDoubleRowClick}
/>
</div>
</>
);
};

export { OrdersTable };
Loading

0 comments on commit aa3b498

Please sign in to comment.