Merge branch 'staging_environment' of gitlab.com:brandx/bxt-app into staging_environment

This commit is contained in:
csawatzky 2025-05-22 14:00:45 -06:00
commit 625a1f573d
3 changed files with 133 additions and 52 deletions

View file

@ -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<string>("")
const [order, setOrder] = useState<"asc" | "desc">("asc")
const [selectedBin, setSelectedBin] = useState<jsonBin | undefined>();
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<jsonBin>[] = [
{
title: "Manufacturer",
render: (row: jsonBin) => <Box padding={2}>{row.Manufacturer}</Box>
},
{
title: "Model",
render: (row: jsonBin) => <Box padding={2}>{row.Model}</Box>
},
{
title: "Capacity",
render: (row: jsonBin) => <Box padding={2}>{row.Capacity}</Box>
},
{
title: "Hopper Angle",
render: (row: jsonBin) => <Box padding={2}>{row.HopperAngle}</Box>
},
{
title: "Diameter " + (getDistanceUnit() === pond.DistanceUnit.DISTANCE_UNIT_FEET ? "(ft)" : "(m)"),
render: (row: jsonBin) => <Box padding={2}>{distanceConversion(row.Diameter).toFixed(2)}</Box>
},
{
title: "Sidewall" + (getDistanceUnit() === pond.DistanceUnit.DISTANCE_UNIT_FEET ? "(ft)" : "(m)"),
render: (row: jsonBin) => <Box padding={2}>{distanceConversion(row.Sidewall).toFixed(2)}</Box>
},
{
title: "Peak Height" + (getDistanceUnit() === pond.DistanceUnit.DISTANCE_UNIT_FEET ? "(ft)" : "(m)"),
render: (row: jsonBin) => <Box padding={2}>{distanceConversion(row.Peak).toFixed(2)}</Box>
},
{
title: "Eave To Peak" + (getDistanceUnit() === pond.DistanceUnit.DISTANCE_UNIT_FEET ? "(ft)" : "(m)"),
render: (row: jsonBin) => <Box padding={2}>{distanceConversion(row.EaveToPeak).toFixed(2)}</Box>
},
{
title: "Roof Angle",
render: (row: jsonBin) => <Box padding={2}>{row.RoofAngle}</Box>
},
{
title: "Rings",
render: (row: jsonBin) => <Box padding={2}>{row.Rings}</Box>
}
]
{
title: "Manufacturer",
sortKey: "Manufacturer",
render: (row: jsonBin) => <Box padding={2}>{row.Manufacturer}</Box>
},
{
title: "Model",
sortKey: "Model",
render: (row: jsonBin) => <Box padding={2}>{row.Model}</Box>
},
{
title: "Capacity",
sortKey: "Capacity",
render: (row: jsonBin) => <Box padding={2}>{row.Capacity}</Box>
},
{
title: "Hopper Angle",
sortKey: "HopperAngle",
render: (row: jsonBin) => <Box padding={2}>{row.HopperAngle}</Box>
},
{
title: "Diameter " + (getDistanceUnit() === pond.DistanceUnit.DISTANCE_UNIT_FEET ? "(ft)" : "(m)"),
sortKey: "Diameter",
render: (row: jsonBin) => <Box padding={2}>{distanceConversion(row.Diameter).toFixed(2)}</Box>
},
{
title: "Sidewall" + (getDistanceUnit() === pond.DistanceUnit.DISTANCE_UNIT_FEET ? "(ft)" : "(m)"),
sortKey: "Sidewall",
render: (row: jsonBin) => <Box padding={2}>{distanceConversion(row.Sidewall).toFixed(2)}</Box>
},
{
title: "Peak Height" + (getDistanceUnit() === pond.DistanceUnit.DISTANCE_UNIT_FEET ? "(ft)" : "(m)"),
sortKey: "PeakHeight",
render: (row: jsonBin) => <Box padding={2}>{distanceConversion(row.Peak).toFixed(2)}</Box>
},
{
title: "Eave To Peak" + (getDistanceUnit() === pond.DistanceUnit.DISTANCE_UNIT_FEET ? "(ft)" : "(m)"),
sortKey: "EaveToPeak",
render: (row: jsonBin) => <Box padding={2}>{distanceConversion(row.EaveToPeak).toFixed(2)}</Box>
},
{
title: "Roof Angle",
sortKey: "RoofAngle",
render: (row: jsonBin) => <Box padding={2}>{row.RoofAngle}</Box>
},
{
title: "Rings",
sortKey: "Rings",
render: (row: jsonBin) => <Box padding={2}>{row.Rings}</Box>
}
]
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}
/>
);
};

View file

@ -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<T> {
cellStyle?: SxProps<Theme>;
hidden?: boolean;
render: (row: T) => JSX.Element;
sortKey?: string;
disableSort?: boolean;
}
interface Props<T> {
@ -92,6 +94,10 @@ interface Props<T> {
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<React.SetStateAction<string>>;
order?: string;
setOrder?: React.Dispatch<React.SetStateAction<"asc" | "desc">>;
}
export default function ResponsiveTable<T extends Object>(props: Props<T>) {
@ -120,12 +126,17 @@ export default function ResponsiveTable<T extends Object>(props: Props<T>) {
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<T extends Object>(props: Props<T>) {
setFilterList(newFilterList)
}
const columnClick = (column: Column<T>) => {
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<T>) => {
if (!orderBy) return
const sortKey = column.sortKey ? column.sortKey : column.title.toLowerCase()
if (sortKey === orderBy) {
if (order === "asc") {
return <ArrowDownward fontSize="small"/>
} else {
return <ArrowUpward fontSize="small"/>
}
}
return null;
}
return (
<TableContainer className={classes.tableContainer} component={Paper}>
<TableContainer className={classes.tableContainer} component={Paper}>
<Table>
<TableHead>
<TableRow sx={ !title && !setSearchText ? { display: "none" } : undefined}>
<TableCell colSpan={10000} sx={{ border: "none" }}>
<Grid2 container justifyContent="space-between">
@ -418,7 +447,6 @@ export default function ResponsiveTable<T extends Object>(props: Props<T>) {
</TableCell>
</TableRow>
}
<TableRow sx={ hideKeys ? { display: "none" } : undefined } >
{ rowSelect && <TableCell></TableCell> }
@ -427,8 +455,14 @@ export default function ResponsiveTable<T extends Object>(props: Props<T>) {
columns.map((column, index) => {
if (filterList.includes(column.title) || column.hidden) return null
return (
<TableCell key={"header-cell-"+index}>
{column.title}
<TableCell
key={"header-cell-"+index}
onClick={() => !column.disableSort && columnClick(column)}
sx={{ cursor: column.disableSort ? "default" : "pointer" }}
>
<Grid2 container spacing={1} alignItems={"center"}>
{column.title} {showOrderIcon(column)}
</Grid2>
</TableCell>
)
})

View file

@ -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<Device[]>([])
@ -293,10 +293,15 @@ export default function Devices() {
if (newValue === "never") openGroupSettings(undefined, "add");
};
useEffect(() => {
console.log(devices)
}, [devices])
const columns = (): Column<Device>[] => {
let columns = [
let columns: Column<Device>[] = [
{
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) => <span className={classes.cellContainer}>
{device.settings?.deviceId}
</span>
},
{
title: "Device",
sortKey: "name",
render: (device: Device) => {
return (
<Box className={classes.cellContainer}>
@ -346,6 +353,7 @@ export default function Devices() {
},
{
title: "Tags",
sortKey: "tags",
render: (device: Device) => {
return (
<Grid2 container spacing={1} className={classes.tagsCellContainer}>
@ -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() && <GroupActions removeCallback={removeGroupCallback} group={getGroup()} permissions={groupPermissions} devices={devices} refreshCallback={loadDevices}/>}
order={order}
setOrder={setOrder}
orderBy={orderBy}
setOrderBy={setOrderBy}
/>
<ProvisionDevice
isOpen={isProvisionDialogOpen}