-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Ieee 269 create table for orders page (#500)
* 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
1 parent
d4c7cd5
commit 085ef30
Showing
15 changed files
with
708 additions
and
73 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 3 additions & 0 deletions
3
...ite/dashboard/frontend/src/assets/images/icons/statusIcons/cancelled-status.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions
3
...site/dashboard/frontend/src/assets/images/icons/statusIcons/checkout-status.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions
3
...te/dashboard/frontend/src/assets/images/icons/statusIcons/inprogress-status.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions
4
..._site/dashboard/frontend/src/assets/images/icons/statusIcons/pending-status.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions
3
...ashboard/frontend/src/assets/images/icons/statusIcons/readyforpickup-status.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions
3
...e/dashboard/frontend/src/assets/images/icons/statusIcons/unfulfilled-status.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
59 changes: 59 additions & 0 deletions
59
hackathon_site/dashboard/frontend/src/components/orders/OrdersTable/OrdersTable.module.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
98 changes: 98 additions & 0 deletions
98
hackathon_site/dashboard/frontend/src/components/orders/OrdersTable/OrdersTable.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
}); | ||
}); | ||
}); |
132 changes: 132 additions & 0 deletions
132
hackathon_site/dashboard/frontend/src/components/orders/OrdersTable/OrdersTable.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 }; |
Oops, something went wrong.