diff --git a/src/cableEstimator/cableEstimator.tsx b/src/cableEstimator/cableEstimator.tsx index f587efd..960e06a 100644 --- a/src/cableEstimator/cableEstimator.tsx +++ b/src/cableEstimator/cableEstimator.tsx @@ -61,6 +61,8 @@ export default function CableEstimator() { const [tablePage, setTablePage] = useState(0) const [pageSize, setPageSize] = useState(10) const [rowCount, setRowCount] = useState(0) + const [orderBy, setOrderBy] = useState("") + const [order, setOrder] = useState<"asc" | "desc">("asc") const [selectedBin, setSelectedBin] = useState(); const [quoteOpen, setQuoteOpen] = useState(false); const [useCustomBin, setUseCustomBin] = useState(false); @@ -106,47 +108,57 @@ export default function CableEstimator() { const [loading, setLoading] = useState(false) const columns: Column[] = [ - { - title: "Manufacturer", - render: (row: jsonBin) => {row.Manufacturer} - }, - { - title: "Model", - render: (row: jsonBin) => {row.Model} - }, - { - title: "Capacity", - render: (row: jsonBin) => {row.Capacity} - }, - { - title: "Hopper Angle", - render: (row: jsonBin) => {row.HopperAngle} - }, - { - title: "Diameter " + (getDistanceUnit() === pond.DistanceUnit.DISTANCE_UNIT_FEET ? "(ft)" : "(m)"), - render: (row: jsonBin) => {distanceConversion(row.Diameter).toFixed(2)} - }, - { - title: "Sidewall" + (getDistanceUnit() === pond.DistanceUnit.DISTANCE_UNIT_FEET ? "(ft)" : "(m)"), - render: (row: jsonBin) => {distanceConversion(row.Sidewall).toFixed(2)} - }, - { - title: "Peak Height" + (getDistanceUnit() === pond.DistanceUnit.DISTANCE_UNIT_FEET ? "(ft)" : "(m)"), - render: (row: jsonBin) => {distanceConversion(row.Peak).toFixed(2)} - }, - { - title: "Eave To Peak" + (getDistanceUnit() === pond.DistanceUnit.DISTANCE_UNIT_FEET ? "(ft)" : "(m)"), - render: (row: jsonBin) => {distanceConversion(row.EaveToPeak).toFixed(2)} - }, - { - title: "Roof Angle", - render: (row: jsonBin) => {row.RoofAngle} - }, - { - title: "Rings", - render: (row: jsonBin) => {row.Rings} - } - ] + { + title: "Manufacturer", + sortKey: "Manufacturer", + render: (row: jsonBin) => {row.Manufacturer} + }, + { + title: "Model", + sortKey: "Model", + render: (row: jsonBin) => {row.Model} + }, + { + title: "Capacity", + sortKey: "Capacity", + render: (row: jsonBin) => {row.Capacity} + }, + { + title: "Hopper Angle", + sortKey: "HopperAngle", + render: (row: jsonBin) => {row.HopperAngle} + }, + { + title: "Diameter " + (getDistanceUnit() === pond.DistanceUnit.DISTANCE_UNIT_FEET ? "(ft)" : "(m)"), + sortKey: "Diameter", + render: (row: jsonBin) => {distanceConversion(row.Diameter).toFixed(2)} + }, + { + title: "Sidewall" + (getDistanceUnit() === pond.DistanceUnit.DISTANCE_UNIT_FEET ? "(ft)" : "(m)"), + sortKey: "Sidewall", + render: (row: jsonBin) => {distanceConversion(row.Sidewall).toFixed(2)} + }, + { + title: "Peak Height" + (getDistanceUnit() === pond.DistanceUnit.DISTANCE_UNIT_FEET ? "(ft)" : "(m)"), + sortKey: "PeakHeight", + render: (row: jsonBin) => {distanceConversion(row.Peak).toFixed(2)} + }, + { + title: "Eave To Peak" + (getDistanceUnit() === pond.DistanceUnit.DISTANCE_UNIT_FEET ? "(ft)" : "(m)"), + sortKey: "EaveToPeak", + render: (row: jsonBin) => {distanceConversion(row.EaveToPeak).toFixed(2)} + }, + { + title: "Roof Angle", + sortKey: "RoofAngle", + render: (row: jsonBin) => {row.RoofAngle} + }, + { + title: "Rings", + sortKey: "Rings", + render: (row: jsonBin) => {row.Rings} + } + ] useEffect(()=>{ setLoading(true) @@ -154,6 +166,21 @@ export default function CableEstimator() { let clone: jsonBin[] = cloneDeep(binOptions) //having the search filter in here is quite inefficient as it will filter each time you change pages as well //TODO: re-work the filter so it only filters when the search is changed + + // sort the array according to orderBy + clone.sort((a, b) => { + if (Object.keys(a).includes(orderBy)) { + let by = orderBy as keyof jsonBin + if (typeof(a[by]) === "string") { + // return a.localeCompare(b) + return (a[by].localeCompare(b[by] as string)) + } + return ((b[by] as any) - (a[by] as any)) + } + return 0 + }) + if (order === "desc") clone.reverse() + if(tableSearch !== ""){ //filter the data in the cloned list clone = clone.filter(bin => { @@ -175,7 +202,7 @@ export default function CableEstimator() { let rows = clone.splice(tablePage * pageSize, pageSize) setDisplayedRows(rows) setLoading(false) - },[binOptions, tablePage, pageSize, tableSearch]) + },[binOptions, tablePage, pageSize, tableSearch, orderBy, order]) const binOpTable = () => { return ( @@ -193,6 +220,10 @@ export default function CableEstimator() { handleRowsPerPageChange={(e) => {setPageSize(e.target.value)}} rows={displayedRows} total={rowCount} + order={order} + setOrder={setOrder} + orderBy={orderBy} + setOrderBy={setOrderBy} /> ); }; diff --git a/src/common/ResponsiveTable.tsx b/src/common/ResponsiveTable.tsx index de23f1b..3f637d3 100644 --- a/src/common/ResponsiveTable.tsx +++ b/src/common/ResponsiveTable.tsx @@ -1,6 +1,6 @@ import { Box, Card, Checkbox, CircularProgress, Collapse, darken, Divider, Grid2, IconButton, InputAdornment, ListItemButton, ListItemIcon, ListItemText, Menu, MenuItem, Paper, SxProps, Table, TableBody, TableCell, TableContainer, TableHead, TablePagination, TableRow, TextField, Theme, Typography } from "@mui/material"; import { makeStyles } from "@mui/styles"; -import { ChevronRight, Close, Search, ViewColumn } from "@mui/icons-material" +import { ArrowDownward, ArrowUpward, ChevronRight, Close, Search, ViewColumn } from "@mui/icons-material" import { useEffect, useState } from "react"; import React from "react"; import { useMobile } from "hooks"; @@ -64,6 +64,8 @@ export interface Column { cellStyle?: SxProps; hidden?: boolean; render: (row: T) => JSX.Element; + sortKey?: string; + disableSort?: boolean; } interface Props { @@ -92,6 +94,10 @@ interface Props { customElement?: JSX.Element //element that will be placed in the table head between the table actions and the column header rows mobileView?: boolean //can be used to force the table to use its mobile view for narrow containing elements ie, drawer hidePagination?: boolean //when passed in will hide the pagination at the bottom of the table + orderBy?: string; + setOrderBy?: React.Dispatch>; + order?: string; + setOrder?: React.Dispatch>; } export default function ResponsiveTable(props: Props) { @@ -120,12 +126,17 @@ export default function ResponsiveTable(props: Props) { selectedRows, customElement, mobileView, - hidePagination + hidePagination, + orderBy, + setOrderBy, + order, + setOrder } = props const classes = useStyles(); const columns = typeof(props.columns) === "function" ? props.columns() : props.columns const subtitle = typeof(props.subtitle) === "function" ? props.subtitle() : props.subtitle const title = typeof(props.title) === "function" ? props.title() : props.title + // const order = props.order ? props.order : "asc" const isMobile = useMobile() @@ -373,12 +384,30 @@ export default function ResponsiveTable(props: Props) { setFilterList(newFilterList) } + const columnClick = (column: Column) => { + const sortKey = column.sortKey ? column.sortKey : column.title.toLowerCase() + // console.log(column.sortKey) + if (setOrderBy) setOrderBy(sortKey) + if (setOrder && order) setOrder(order === "asc" ? "desc" : "asc") + } + + const showOrderIcon = (column: Column) => { + if (!orderBy) return + const sortKey = column.sortKey ? column.sortKey : column.title.toLowerCase() + if (sortKey === orderBy) { + if (order === "asc") { + return + } else { + return + } + } + return null; + } return ( - + - @@ -418,7 +447,6 @@ export default function ResponsiveTable(props: Props) { } - { rowSelect && } @@ -427,8 +455,14 @@ export default function ResponsiveTable(props: Props) { columns.map((column, index) => { if (filterList.includes(column.title) || column.hidden) return null return ( - - {column.title} + !column.disableSort && columnClick(column)} + sx={{ cursor: column.disableSort ? "default" : "pointer" }} + > + + {column.title} {showOrderIcon(column)} + ) }) diff --git a/src/pages/Devices.tsx b/src/pages/Devices.tsx index c4239c5..09c9ae6 100644 --- a/src/pages/Devices.tsx +++ b/src/pages/Devices.tsx @@ -92,8 +92,8 @@ export default function Devices() { const [devicesLoading, setDevicesLoading] = useState(false) const [limit, setLimit] = useState(10); const [page, setPage] = useState(0); - const [order, ] = useState<"asc" | "desc">("asc"); - const [orderBy, ] = useState("name"); + const [order, setOrder] = useState<"asc" | "desc">("asc"); + const [orderBy, setOrderBy] = useState("name"); const [search, setSearch] = useState(""); const [total, setTotal] = useState(0); const [devices, setDevices] = useState([]) @@ -293,10 +293,15 @@ export default function Devices() { if (newValue === "never") openGroupSettings(undefined, "add"); }; + useEffect(() => { + console.log(devices) + }, [devices]) + const columns = (): Column[] => { - let columns = [ + let columns: Column[] = [ { title: "State", + // sortKey: "state", render: (device: Device) => { const status = device.status ?? pond.DeviceStatus.create() const deviceStateHelper = getDeviceStateHelper(status.state); @@ -309,12 +314,14 @@ export default function Devices() { }, { title: "ID", + sortKey: "deviceId", render: (device: Device) => {device.settings?.deviceId} }, { title: "Device", + sortKey: "name", render: (device: Device) => { return ( @@ -346,6 +353,7 @@ export default function Devices() { }, { title: "Tags", + sortKey: "tags", render: (device: Device) => { return ( @@ -362,6 +370,8 @@ export default function Devices() { if (hasPlenums) { columns.push({ title: "Plenum", + // sortKey: "hi", + // disableSort: true, render: (device: Device) => { if (device.status.plenum?.temperature) { return ( @@ -380,6 +390,8 @@ export default function Devices() { if (hasSen5x) { columns.push({ title: "Sen5X", + // sortKey: "hi", + // disableSort: true, render: (device: Device) => { if (device.status.sen5x?.temperature) { return ( @@ -532,6 +544,10 @@ export default function Devices() { setSearchText={setSearch} isLoading={devicesLoading} actions={getGroup() && } + order={order} + setOrder={setOrder} + orderBy={orderBy} + setOrderBy={setOrderBy} />