diff --git a/src/cableEstimator/cableEstimator.tsx b/src/cableEstimator/cableEstimator.tsx index 960e06a..17f909c 100644 --- a/src/cableEstimator/cableEstimator.tsx +++ b/src/cableEstimator/cableEstimator.tsx @@ -184,16 +184,16 @@ export default function CableEstimator() { if(tableSearch !== ""){ //filter the data in the cloned list clone = clone.filter(bin => { - if(tableSearch.toLowerCase() === bin.Manufacturer.toLowerCase()) return true - if(tableSearch.toLowerCase() === bin.Model.toLowerCase()) return true - if(tableSearch.toLowerCase() === bin.Capacity.toString().toLowerCase()) return true - if(tableSearch.toLowerCase() === bin.HopperAngle.toString().toLowerCase()) return true - if(tableSearch.toLowerCase() === bin.Diameter.toString().toLowerCase()) return true - if(tableSearch.toLowerCase() === bin.Sidewall.toString().toLowerCase()) return true - if(tableSearch.toLowerCase() === bin.Peak.toString().toLowerCase()) return true - if(tableSearch.toLowerCase() === bin.EaveToPeak.toString().toLowerCase()) return true - if(tableSearch.toLowerCase() === bin.RoofAngle.toString().toLowerCase()) return true - if(tableSearch.toLowerCase() === bin.Rings.toString().toLowerCase()) return true + if(bin.Manufacturer.toLowerCase().includes(tableSearch.toLowerCase())) return true + if(bin.Model.toLowerCase().includes(tableSearch.toLowerCase())) return true + if(bin.Capacity.toString().toLowerCase().includes(tableSearch.toLowerCase())) return true + if(bin.HopperAngle.toString().toLowerCase().includes(tableSearch.toLowerCase())) return true + if(bin.Diameter.toString().toLowerCase().includes(tableSearch.toLowerCase())) return true + if(bin.Sidewall.toString().toLowerCase().includes(tableSearch.toLowerCase())) return true + if(bin.Peak.toString().toLowerCase().includes(tableSearch.toLowerCase())) return true + if(bin.EaveToPeak.toString().toLowerCase().includes(tableSearch.toLowerCase())) return true + if(bin.RoofAngle.toString().toLowerCase().includes(tableSearch.toLowerCase())) return true + if(bin.Rings.toString().toLowerCase().includes(tableSearch.toLowerCase())) return true return false }) } diff --git a/src/common/QrCodeGenerator.tsx b/src/common/QrCodeGenerator.tsx index 8d5ddcb..27eaff0 100644 --- a/src/common/QrCodeGenerator.tsx +++ b/src/common/QrCodeGenerator.tsx @@ -1,4 +1,4 @@ -import { Box, Button, DialogActions, DialogContent, DialogTitle } from "@mui/material"; +import { Box, Button, Checkbox, DialogActions, DialogContent, DialogTitle, Typography } from "@mui/material"; // import MaterialTable from "material-table"; import React, { useEffect, useState } from "react"; import ResponsiveDialog from "./ResponsiveDialog"; @@ -36,7 +36,7 @@ export default function(props: Props) { width: 500 }, viewerMobile: { - height: "100%", + height: 500, width: "100%" }, page: { @@ -103,12 +103,12 @@ export default function(props: Props) { selected.push(row) }else{ //remove the page/index from the map if(pageSelect){ - pageSelect.splice(pageSelect.indexOf(index)) + pageSelect.splice(pageSelect.indexOf(index), 1) } //remove the selected object from the array selected.forEach((codeKey, index) => { if(row.key === codeKey.key){ - selected.splice(index) + selected.splice(index, 1) } }) } @@ -137,35 +137,27 @@ export default function(props: Props) { total={keyList.length} setPage={(page) => {setTablePage(page)}} handleRowsPerPageChange={handleRowsPerPageChange} + loadMore={() => { + let clone: QrCodeKey[] = cloneDeep(keyList) + let current = cloneDeep(displayedRows) + let newRows = clone.splice(current.length, pageSize) + setDisplayedRows(current.concat(newRows)) + }} + renderMobile={(row, index) => { + return ( + + { + rowSelected(row, index, checked) + }} + /> + + {row.name} + + + ) + }} /> - {/* { - setSelectedKeys(data); - }} - columns={[ - { - cellStyle: { - padding: 0 - }, - title: "Name", - field: "name" - } - ]}> */} ); }; diff --git a/src/common/ResponsiveTable.tsx b/src/common/ResponsiveTable.tsx index de6ae6a..b2fa240 100644 --- a/src/common/ResponsiveTable.tsx +++ b/src/common/ResponsiveTable.tsx @@ -85,7 +85,7 @@ interface Props { multiGutter?: boolean; rowsPerPageOptions?: number[]; noDataMessage?: string; - renderMobile?: (row: T) => JSX.Element; + renderMobile?: (row: T, index: number) => JSX.Element; hideKeys?: boolean; onRowClick?: (row: T) => void; actions?: string | JSX.Element; @@ -272,9 +272,9 @@ export default function ResponsiveTable(props: Props) { return String(val).charAt(0).toUpperCase() + String(val).slice(1); } - const renderMobileRow = (row: T) => { + const renderMobileRow = (row: T, index: number) => { if (renderMobile) return ( - renderMobile(row) + renderMobile(row, index) ) if (columns) { return ( @@ -329,7 +329,7 @@ export default function ResponsiveTable(props: Props) { {rows.map((row, index) => { return ( onRowClick&&onRowClick(row)}> - {renderMobileRow(row)} + {renderMobileRow(row, index)} {renderGutter && <> diff --git a/src/common/SmartBreadcrumb.tsx b/src/common/SmartBreadcrumb.tsx index be1d0fe..a30d60a 100644 --- a/src/common/SmartBreadcrumb.tsx +++ b/src/common/SmartBreadcrumb.tsx @@ -7,6 +7,7 @@ import { LinkProps, Skeleton, Theme, + Tooltip, Typography, useTheme } from "@mui/material"; @@ -334,22 +335,23 @@ export default function SmartBreadcrumb(props: Props) { const linkComponent = (to: string, label: string, lastPath: boolean) => { return ( - - {label} - - } - className={classes.chip} - /> + + + {label} + + } + className={classes.chip} + /> + ); }; diff --git a/src/contract/contractTransactionTable.tsx b/src/contract/contractTransactionTable.tsx index 4508377..ebee9ba 100644 --- a/src/contract/contractTransactionTable.tsx +++ b/src/contract/contractTransactionTable.tsx @@ -369,6 +369,12 @@ export default function ContractTransactionTable(props: Props) { total={tableData.length} handleRowsPerPageChange={(e)=>{setPageSize(e.target.value)}} setPage={(page)=>{setTablePage(page)}} + loadMore={()=>{ + let clone: Transaction[] = cloneDeep(tableData) + let current = cloneDeep(displayData) + let newRows = clone.splice(current.length, pageSize) + setDisplayData(current.concat(newRows)) + }} /> {/* )} - {user.allowedTo("provision") && } + {} ); }; diff --git a/src/device/DeviceTags.tsx b/src/device/DeviceTags.tsx index 82981d1..7806a59 100644 --- a/src/device/DeviceTags.tsx +++ b/src/device/DeviceTags.tsx @@ -68,6 +68,7 @@ function AddDeviceTag(props: AddDeviceTagProps) { addTagToDevice(tag)} /> )); + return ( setOpen(true)} size="small"> diff --git a/src/gate/GateFlowGraph.tsx b/src/gate/GateFlowGraph.tsx index 9e05ea9..6e060c6 100644 --- a/src/gate/GateFlowGraph.tsx +++ b/src/gate/GateFlowGraph.tsx @@ -127,6 +127,7 @@ export default function GateFlowGraph(props: Props) { setRuntime(moment.duration(runtime)); let state = false; if ( + data.length > 1 && data[data.length - 1].value > gate.lowerFlow() && data[data.length - 1].value < gate.upperFlow() ) { diff --git a/src/gate/GateList.tsx b/src/gate/GateList.tsx index 8f0c8bc..5c548e2 100644 --- a/src/gate/GateList.tsx +++ b/src/gate/GateList.tsx @@ -12,6 +12,7 @@ import AddGateFab from "./AddGateFab"; import GateSettings from "./GateSettings"; import { useGateAPI, useGlobalState } from "providers"; import { Settings } from "@mui/icons-material"; +import { cloneDeep } from "lodash"; interface Props { //gates: Gate[]; @@ -219,6 +220,19 @@ export default function GateList(props: Props) { onRowClick={(gate) => {goToGate(gate)}} hideKeys={isMobile || props.useMobile} setSearchText={(search) => setSearch(search)} + loadMore={()=>{ + let current = cloneDeep(gates) + let keys = undefined + let types = undefined + if(currentTerminal) { + keys = [currentTerminal] + types = ["terminal"] + } + gateAPI.listGates(pageSize, current.length , "asc", "name", search, undefined, keys, types, undefined, undefined, as).then(resp => { + let newGates = resp.data.gates.map(gate => Gate.create(gate)) + setGates(current.concat(newGates)) + }) + }} /> ) }; diff --git a/src/maps/mapDrawers/GroupDrawer.tsx b/src/maps/mapDrawers/GroupDrawer.tsx index 4048245..e9d2768 100644 --- a/src/maps/mapDrawers/GroupDrawer.tsx +++ b/src/maps/mapDrawers/GroupDrawer.tsx @@ -3,6 +3,7 @@ import DisplayDrawer from "common/DisplayDrawer"; import ResponsiveTable from "common/ResponsiveTable"; //import DeviceCardList from "device/DeviceCardList"; import { useGroupAPI, useSnackbar } from "hooks"; +import { cloneDeep } from "lodash"; import { Group } from "models"; import moment from "moment"; import { describePower } from "pbHelpers/Power"; @@ -28,18 +29,19 @@ export default function GroupDrawer(props: Props) { const { openSnack } = useSnackbar(); const [grpDevs, setGrpDevs] = useState([]); const navigate = useNavigate(); - const [tablePage, setTablePage] = useState(0) - const [pageSize, setPageSize] = useState(10) + const loadLimit = 10 + const [groupTotal, setGroupTotal] = useState(0) const loadDevs = useCallback(() => { groupAPI - .listGroupDevices(group.id(), 100, 0, "asc", undefined, true) + .listGroupDevices(group.id(), loadLimit, 0, "asc", undefined, true) .then(resp => { const groupDevices: pond.ComprehensiveDevice[] = or( resp.data.comprehensiveDevices, [] ).map(d => pond.ComprehensiveDevice.fromObject(d)); setGrpDevs(groupDevices); + setGroupTotal(resp.data.total) }) .catch(err => { openSnack("Failed to get devices for group"); @@ -174,21 +176,37 @@ export default function GroupDrawer(props: Props) { } const drawerBody = () => { - // TODO: will need to update the rows and setPage functions if we decide to have the table only show a number of items matching its page size rether than everything passed in return ( - page={tablePage} - pageSize={pageSize} + page={0} + pageSize={loadLimit} onRowClick={toDevice} - handleRowsPerPageChange={()=>{}} - setPage={()=>{}} - total={0} + total={groupTotal} rows={grpDevs} renderMobile={mobile} renderGutter={gutter} gutterPadding={0} mobileView + loadMore={() => { + let current = cloneDeep(grpDevs) + groupAPI + .listGroupDevices(group.id(), loadLimit, current.length, "asc", undefined, true) + .then(resp => { + let newDevices: pond.ComprehensiveDevice[] = or( + resp.data.comprehensiveDevices, + [] + ).map(d => pond.ComprehensiveDevice.fromObject(d)); + setGrpDevs(current.concat(newDevices)); + setGroupTotal(resp.data.total) + }) + .catch(err => { + openSnack("Failed to get devices for group"); + }); + }} + //these two function are uneccessary as this will always be in a list as mobile view + handleRowsPerPageChange={()=>{}} + setPage={()=>{}} /> ) diff --git a/src/objectHeater/ObjectHeaterCard.tsx b/src/objectHeater/ObjectHeaterCard.tsx index 4255b1f..651ae7c 100644 --- a/src/objectHeater/ObjectHeaterCard.tsx +++ b/src/objectHeater/ObjectHeaterCard.tsx @@ -371,7 +371,7 @@ export default function ObjectHeaterCard(props: Props) { }else { return ( - + { setOpenQrGenerator(true); + setBinMenuAnchorEl(null) }}> Generate QR Codes diff --git a/src/pages/Firmware.tsx b/src/pages/Firmware.tsx index ecd628a..b3a7c58 100644 --- a/src/pages/Firmware.tsx +++ b/src/pages/Firmware.tsx @@ -39,6 +39,7 @@ import { useGlobalState } from "providers"; import { useCallback, useEffect, useState } from "react"; import { downloadFile } from "utils/download"; import { or } from "utils/types"; +import { cloneDeep } from "lodash"; const useStyles = makeStyles((theme: Theme) => { const themeType = useThemeType() @@ -463,6 +464,22 @@ export default function Firmware() { columns={columns(width === "xs" || width === "sm")} setPage={(page)=>{setTablePage(page)}} handleRowsPerPageChange={(e)=>{setPageSize(e.target.value)}} + loadMore={() => { + let current = cloneDeep(tableData) + firmwareAPI.listFirmware(pageSize, current.length, "desc", "version", searchText) + .then((response) => { + let newRows: FirmwareModel[] = or(response.data.firmware, []).map( + (firmwareData: any) => { + return FirmwareModel.create(pond.Firmware.fromObject(firmwareData)); + } + ); + setTableData(current.concat(newRows)) + }) + .catch(() => { + //failed to load + error("Failed to load more firmware versions") + }); + }} /> { return ({ @@ -241,6 +242,36 @@ export default function GroupPage() { onRowClick={toDevice} setSearchText={setSearch} isLoading={loadingDevices} + loadMore={()=>{ + let current = cloneDeep(devices) + setLoadingDevices(true) + deviceAPI.list( + limit, + current.length, + order, + orderBy, + search, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + getKeys(), + getTypes(), + undefined, + undefined, + as + ).then(resp => { + let newDevices: Device[] = []; + resp.data.devices.forEach(device => { + newDevices.push(Device.create(device)) + }) + setDevices(current.concat(newDevices)) + }).finally(() => { + setLoadingDevices(false) + }) + }} /> ) diff --git a/src/pages/Groups.tsx b/src/pages/Groups.tsx index 0d06947..acc6673 100644 --- a/src/pages/Groups.tsx +++ b/src/pages/Groups.tsx @@ -13,6 +13,7 @@ import { or } from "utils/types"; import PageContainer from "./PageContainer"; import GroupSettings from "group/GroupSettings"; import { green } from "@mui/material/colors"; +import { cloneDeep } from "lodash"; const useStyles = makeStyles((theme: Theme) => { const isMobile = useMobile() @@ -163,6 +164,15 @@ export default function GroupsPage() { isLoading={loading} setSearchText={setSearch} onRowClick={handleRowClick} + loadMore={()=>{ + let current = cloneDeep(groups) + setLoading(true) + groupAPI.listGroups(limit, current.length, order, orderBy, search).then(resp => { + setGroups(or(current.concat(resp.data.groups), current)) + }).finally(() => { + setLoading(false) + }) + }} /> {setTablePage(page)}} handleRowsPerPageChange={handleChange} onRowClick={goToHeater} - /> - ) - }; - - const mobileList = () => { - return ( - - {heaters.map(h => ( - { + let current = cloneDeep(heaters) + objectHeaterAPI + .listObjectHeatersPageData(pageSize, current.length, undefined, undefined, searchText, as) + .then(resp => { + setHeaters(heaters.concat(resp.data.heaterData)); + }); + }} + renderMobile={(row)=>{ + return ( + { setHeaterToLink(heater); setOpenLinkDrawer(true); @@ -131,9 +134,10 @@ export default function Heaters() { setDevices(d); }} /> - ))} - - ); + ) + }} + /> + ) }; return ( @@ -180,7 +184,7 @@ export default function Heaters() { }} pulse={heaters.length < 1} /> - {isMobile ? mobileList() : desktopTable()} + {desktopTable()} ); } diff --git a/src/pages/Mines.tsx b/src/pages/Mines.tsx index 5e73bae..fcaeac6 100644 --- a/src/pages/Mines.tsx +++ b/src/pages/Mines.tsx @@ -14,6 +14,7 @@ import ResponsiveTable, { Column } from "common/ResponsiveTable"; import { useNavigate } from "react-router-dom"; import { or } from "utils"; import { useGlobalState } from "providers"; +import { cloneDeep } from "lodash"; interface MineRow { mine: pond.Mine; @@ -110,6 +111,24 @@ export default function Mines() { hideKeys isLoading={isLoading} onRowClick={onRowClick} + loadMore={()=>{ + let current = cloneDeep(mineTableData) + setIsLoading(true); + mineAPI + .listMines(pageSize, current.length, "desc", undefined, searchValue, false, as) + .then(resp => { + let newMines: MineRow[] = []; + resp.data.mines.forEach(mine => { + newMines.push({ + mine: mine + }); + }); + setMineTableData(current.concat(newMines)); + }) + .finally(() => { + setIsLoading(false); + }); + }} /> diff --git a/src/pages/Sites.tsx b/src/pages/Sites.tsx index e749643..b17fe0e 100644 --- a/src/pages/Sites.tsx +++ b/src/pages/Sites.tsx @@ -1,4 +1,4 @@ -import { Avatar, Box, Card, Grid2 as Grid, Switch, Tab, Tabs, Tooltip, Typography } from "@mui/material"; +import { Avatar, Box, Button, Card, Grid2 as Grid, Switch, Tab, Tabs, Tooltip, Typography } from "@mui/material"; import { Group } from "@mui/icons-material"; //import MaterialTable from "material-table"; import { Site, User } from "models"; @@ -12,6 +12,7 @@ import { useMobile } from "hooks"; import JobsiteIcon from "products/Construction/JobSiteIcon"; import ResponsiveTable, { Column } from "common/ResponsiveTable"; import { useNavigate } from "react-router-dom"; +import { cloneDeep } from "lodash"; interface SiteWithData { site: Site; @@ -46,7 +47,7 @@ export default function Sites() { .listSitesPageData(pageSize, tablePage * pageSize, "asc", "siteName", undefined, as) .then(resp => { setTotal(resp.data.total); - buildTableData(resp.data.sites); + setSites(buildTableData(resp.data.sites)); }) .finally(() => { setLoading(false); @@ -71,7 +72,7 @@ export default function Sites() { data.push(newTableData); } }); - setSites([...data]); + return data; }; useEffect(() => { @@ -186,6 +187,19 @@ export default function Sites() { rows={sites} handleRowsPerPageChange={handleTablePage} onRowClick={goToSite} + loadMore={()=>{ + let current = cloneDeep(sites) + setLoading(true); + siteAPI + .listSitesPageData(pageSize, current.length, "asc", "siteName", undefined, as) + .then(resp => { + let newData = buildTableData(resp.data.sites); + setSites(current.concat(newData)) + }) + .finally(() => { + setLoading(false); + }); + }} /> ) }; @@ -208,6 +222,20 @@ export default function Sites() { setCurrentTab(newValue); }; + const loadMore = () => { + let current = cloneDeep(sites) + setLoading(true); + siteAPI + .listSitesPageData(pageSize, current.length, "asc", "siteName", undefined, as) + .then(resp => { + let newData = buildTableData(resp.data.sites); + setSites(current.concat(newData)) + }) + .finally(() => { + setLoading(false); + }); + } + const mobileView = () => { return ( @@ -230,7 +258,7 @@ export default function Sites() { siteCards.push( { goToSite(site); }}> @@ -297,6 +325,9 @@ export default function Sites() { return siteCards; })} + {sites.length < total && + + } ); }; diff --git a/src/pbHelpers/ComponentTypes/DragerGasDongle.ts b/src/pbHelpers/ComponentTypes/DragerGasDongle.ts index a8f2090..db71771 100644 --- a/src/pbHelpers/ComponentTypes/DragerGasDongle.ts +++ b/src/pbHelpers/ComponentTypes/DragerGasDongle.ts @@ -112,7 +112,7 @@ export function dragerGasDongle(subtype: number = 0): ComponentTypeExtension { excludedNodes?: number[] ): Summary[] => { //if it is O2 display percentage rather than ppm - if (subtype === quack.DragerGasDongleSubtype.DRAGER_GAS_DONGLE_SUBTYPE_O2) { + if (subtype === quack.DragerGasDongleSubtype.DRAGER_GAS_DONGLE_SUBTYPE_O2 || subtype === quack.DragerGasDongleSubtype.DRAGER_GAS_DONGLE_SUBTYPE_LEL) { let summaries: Summary[] = []; measurements.measurementsFor.forEach(measurement => { let describer = describeMeasurement( diff --git a/src/providers/pond/deviceAPI.tsx b/src/providers/pond/deviceAPI.tsx index 27ea776..6a940e2 100644 --- a/src/providers/pond/deviceAPI.tsx +++ b/src/providers/pond/deviceAPI.tsx @@ -8,6 +8,7 @@ import { useGlobalState } from "providers"; import moment from "moment"; import { or } from "utils/types"; import { dateRange } from "providers/http"; +import { getContextKeys, getContextTypes } from "pbHelpers/Context"; export interface IDeviceAPIContext { add: (name: string, description: string, backpack: pond.BackpackSettings, otherTeam?: string) => Promise>; @@ -725,8 +726,12 @@ export default function DeviceProvider(props: PropsWithChildren) { }; const tag = (id: number, tag: string) => { + const keys = getContextKeys() + const types = getContextTypes() + let url = "/devices/" + id + "/tags/" + tag + "?keys=" + keys + "&types=" + types + if (as && !keys.includes(as)) url += "&as=" + as return new Promise((resolve, reject) => { - put(pondURL("/devices/" + id + "/tags/" + tag), {}).then(resp => { + put(pondURL(url), {}).then(resp => { return resolve(resp) }).catch(err => { return reject(err) diff --git a/src/providers/pond/tagAPI.tsx b/src/providers/pond/tagAPI.tsx index 235e622..071591b 100644 --- a/src/providers/pond/tagAPI.tsx +++ b/src/providers/pond/tagAPI.tsx @@ -33,8 +33,16 @@ export default function TagProvider(props: PropsWithChildren) { const [{ as }] = useGlobalState() const addTag = (tag: pond.TagSettings) => { + let keys = getContextKeys() + let types = getContextTypes() + let url = "/tags" + if (keys.includes(as)) { + url += "?keys=" + keys + "&types=" + types + } else { + url += "?as=" + as + "&keys=" + keys + "&types=" + types + } return new Promise((resolve, reject) => { - post(pondURL("/tags"), tag).then(resp => { + post(pondURL(url), tag).then(resp => { return resolve(resp) }).catch(err => { return reject(err)