Skip to content

Commit

Permalink
Ieee 269 create table for orders page (#500)
Browse files Browse the repository at this point in the history
* orders table init

* adding status icon

* adding event handler for row double click & files

* adding more statuses

* adding unit tests

* reverting yarn lock, seemed to have issues with testing

* changing node version to 16 for mui data grid

* cleaning up icon code

* updating github actions

* trying again

* one more time...

* trying this...

* Update main.yml

* adding styles to module.scss

* making team id visible

* changing imports

* adding dependencies

* fixing some code & commenting all tests for order cards

* removing mockdata

* commenting out unused vars

* cleaning up code

* adding more tests

* adding handle double row click event

* adding test to check for proper hanlding of double row click

* using type any for valueGetter function

* remove unused vars

* removing comments

* adding style to make the grid white

---------

Co-authored-by: Mustafa <mus2003.abdul@gmail.com>
  • Loading branch information
natapokie and Mustaballer committed Dec 27, 2023
1 parent d4c7cd5 commit 085ef30
Show file tree
Hide file tree
Showing 15 changed files with 708 additions and 73 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
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 @@ -102,7 +102,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 085ef30

Please sign in to comment.