Merge branch 'staging_environment' into hybrid_inventory_control
This commit is contained in:
commit
d17be025cb
40 changed files with 2223 additions and 206 deletions
|
|
@ -1117,6 +1117,7 @@ export default function Bins(props: Props) {
|
|||
<MenuItem
|
||||
onClick={() => {
|
||||
setOpenQrGenerator(true);
|
||||
setBinMenuAnchorEl(null)
|
||||
}}>
|
||||
<Typography>Generate QR Codes</Typography>
|
||||
</MenuItem>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import { pond, quack } from "protobuf-ts/pond";
|
|||
import { cloneDeep } from "lodash";
|
||||
import DeviceOverview from "device/DeviceOverview";
|
||||
import { DeviceAvailabilityMap, FindAvailablePositions, OffsetAvailabilityMap } from "pbHelpers/DeviceAvailability";
|
||||
import AddComponentManualDialog from "component/AddComponentManualDialog";
|
||||
import ComponentCard from "component/ComponentCard";
|
||||
import { sameComponentID, sortComponents } from "pbHelpers/Component";
|
||||
import { isController } from "pbHelpers/ComponentType";
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import {
|
|||
} from "hooks";
|
||||
import InteractionChip from "interactions/InteractionChip";
|
||||
import InteractionSettings from "interactions/InteractionSettings";
|
||||
import { cloneDeep } from "lodash";
|
||||
// import MaterialTable from "material-table";
|
||||
import { Component, Device, deviceScope, Group, Interaction } from "models";
|
||||
import { convertedUnitMeasurement, MeasurementsFor, UnitMeasurement } from "models/UnitMeasurement";
|
||||
|
|
@ -450,7 +451,6 @@ export default function DeviceComponent() {
|
|||
// });
|
||||
})
|
||||
.catch((err: any) => {
|
||||
|
||||
// resolve({
|
||||
// data: [],
|
||||
// page: 0,
|
||||
|
|
@ -613,6 +613,32 @@ export default function DeviceComponent() {
|
|||
pageSize={pageSize}
|
||||
setPage={setPage}
|
||||
handleRowsPerPageChange={handleRowsPerPageChange}
|
||||
loadMore={() => {
|
||||
let currentRows = cloneDeep(rows)
|
||||
let newOffset = rows.length
|
||||
componentAPI.listUnitMeasurements(
|
||||
deviceID,
|
||||
componentID,
|
||||
startDate,
|
||||
endDate,
|
||||
pageSize,
|
||||
newOffset,
|
||||
order,
|
||||
undefined,
|
||||
getContextKeys(),
|
||||
getContextTypes(),
|
||||
showErrors,
|
||||
as)
|
||||
.then(resp => {
|
||||
let newRows = UnitMeasurement.convertMeasurements(
|
||||
resp.data.measurements.map(um => UnitMeasurement.any(um, user))
|
||||
)
|
||||
setRows(currentRows.concat(newRows))
|
||||
}).catch(err => {
|
||||
|
||||
})
|
||||
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -301,6 +301,9 @@ export default function DeviceSupport() {
|
|||
<MenuItem value={pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR_BLUE}>
|
||||
V2 Cellular Blue
|
||||
</MenuItem>
|
||||
<MenuItem value={pond.DevicePlatform.DEVICE_PLATFORM_V2_ETHERNET_BLUE}>
|
||||
V2 Ethernet Blue
|
||||
</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<Button color="primary" onClick={() => updateDevicePlatform(platform)}>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { DeveloperBoard as ProvisionIcon, LibraryAdd as CreateGroupIcon } from "@mui/icons-material";
|
||||
import { Box, Chip, Grid2, IconButton, Skeleton, Tab, Tabs, Theme, Tooltip, Typography, useTheme } from "@mui/material";
|
||||
import { DeveloperBoard as ProvisionIcon, LibraryAdd as CreateGroupIcon, Tune } from "@mui/icons-material";
|
||||
import { Box, Checkbox, Chip, Grid2, IconButton, ListItemIcon, ListItemText, Menu, MenuItem, Skeleton, Tab, Tabs, Theme, Tooltip, Typography, useTheme } from "@mui/material";
|
||||
import { blue, green, orange } from "@mui/material/colors";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import ResponsiveTable, { Column } from "common/ResponsiveTable";
|
||||
|
|
@ -24,6 +24,9 @@ import { Tag as TagUI } from "common/Tag";
|
|||
import moment from "moment";
|
||||
import StatusPlenum from "common/StatusPlenum";
|
||||
import StatusSen5x from "common/StatusSen5x";
|
||||
import StatusDust from "common/StatusDust";
|
||||
import StatusGas from "common/StatusGas";
|
||||
import { cloneDeep } from "lodash";
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) => {
|
||||
return ({
|
||||
|
|
@ -57,14 +60,14 @@ const useStyles = makeStyles((theme: Theme) => {
|
|||
margin: theme.spacing(1),
|
||||
marginLeft: theme.spacing(2),
|
||||
display: "flex",
|
||||
width: theme.spacing(32)
|
||||
// width: theme.spacing(26)
|
||||
// justifyContent: "center",
|
||||
},
|
||||
tagsCellContainer: {
|
||||
margin: theme.spacing(1),
|
||||
marginLeft: theme.spacing(2),
|
||||
display: "flex",
|
||||
width: theme.spacing(24)
|
||||
width: theme.spacing(18)
|
||||
// justifyContent: "center",
|
||||
},
|
||||
green: {
|
||||
|
|
@ -92,14 +95,40 @@ 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">(() => {
|
||||
// Load from sessionStorage on initial render
|
||||
const savedOrder = sessionStorage.getItem('order');
|
||||
return (savedOrder === "asc" || savedOrder === "desc") ? savedOrder : "asc";
|
||||
});
|
||||
|
||||
// Save to sessionStorage whenever order changes
|
||||
useEffect(() => {
|
||||
sessionStorage.setItem('order', order);
|
||||
}, [order]);
|
||||
|
||||
const [orderBy, setOrderBy] = useState(() => {
|
||||
// Load from sessionStorage on initial render
|
||||
const savedOrder = sessionStorage.getItem('orderBy');
|
||||
return (savedOrder && savedOrder?.length > 0) ? savedOrder : "name";
|
||||
});
|
||||
|
||||
// Save to sessionStorage whenever order changes
|
||||
useEffect(() => {
|
||||
sessionStorage.setItem('orderBy', orderBy);
|
||||
}, [orderBy]);
|
||||
|
||||
// const [orderBy, setOrderBy] = useState("name");
|
||||
const [search, setSearch] = useState("");
|
||||
const [total, setTotal] = useState(0);
|
||||
const [devices, setDevices] = useState<Device[]>([])
|
||||
const [isProvisionDialogOpen, setIsProvisionDialogOpen] = useState<boolean>(false);
|
||||
const [hasPlenums, setHasPlenums] = useState(false)
|
||||
const [hasSen5x, setHasSen5x] = useState(false)
|
||||
const [hasCo, setHasCo] = useState(false)
|
||||
const [hasCo2, setHasCo2] = useState(false)
|
||||
const [hasNo2, setHasNo2] = useState(false)
|
||||
const [hasO2, setHasO2] = useState(false)
|
||||
|
||||
const [groupsLoading, setGroupsLoading] = useState(false)
|
||||
const [groups, setGroups] = useState<Group[]>([]);
|
||||
|
|
@ -122,6 +151,26 @@ export default function Devices() {
|
|||
const [groupSettingsIsOpen, setGroupSettingsIsOpen] = useState<boolean>(false);
|
||||
|
||||
const [fieldContains, setFieldContains] = useState<Map<string, string>>(new Map<string, string>())
|
||||
|
||||
const [separateDust, setSeparateDust] = useState(() => {
|
||||
const stored = localStorage.getItem('separateDust');
|
||||
return stored !== null ? stored === 'true' : false;
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem('separateDust', separateDust.toString());
|
||||
}, [separateDust]);
|
||||
|
||||
const [groupGas, setGroupGas] = useState(() => {
|
||||
const stored = localStorage.getItem('groupGas');
|
||||
return stored !== null ? stored === 'true' : false;
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem('groupGas', groupGas.toString());
|
||||
}, [groupGas]);
|
||||
|
||||
const [preferencesAnchor, setPreferencesAnchor] = useState<any>(null)
|
||||
|
||||
const [{ as }] = useGlobalState()
|
||||
|
||||
|
|
@ -250,12 +299,12 @@ export default function Devices() {
|
|||
).then(resp => {
|
||||
let newDevices: Device[] = []
|
||||
resp.data.devices.forEach(device => {
|
||||
if (device.status?.plenum?.temperature) {
|
||||
setHasPlenums(true)
|
||||
}
|
||||
if (device.status?.sen5x?.temperature) {
|
||||
setHasSen5x(true)
|
||||
}
|
||||
if (device.status?.plenum?.temperature) setHasPlenums(true)
|
||||
if (device.status?.sen5x?.temperature) setHasSen5x(true)
|
||||
if (device.status?.o2) setHasO2(true)
|
||||
if (device.status?.co) setHasCo(true)
|
||||
if (device.status?.co2) setHasCo2(true)
|
||||
if (device.status?.no2) setHasNo2(true)
|
||||
newDevices.push(Device.create(device))
|
||||
})
|
||||
setDevices(newDevices)
|
||||
|
|
@ -277,15 +326,40 @@ export default function Devices() {
|
|||
setLimit(event.target.value);
|
||||
};
|
||||
|
||||
const prependToUrl = (prependage: number | string) => {
|
||||
const basePath = location.pathname.replace(/\/$/, "");
|
||||
return(`/${prependage}/${basePath}`.replace(/\/{2,}/g, "/").replace(/(\/[^/]+)\/\1+/g, "$1"));
|
||||
};
|
||||
// const prependToUrl = (prependage: number | string) => {
|
||||
// const basePath = location.pathname.replace(/\/$/, "");
|
||||
// return(`/${prependage}/${basePath}`.replace(/\/{2,}/g, "/").replace(/(\/[^/]+)\/\1+/g, "$1"));
|
||||
// };
|
||||
|
||||
function insertGroupContext(groupID: string, deviceID: string): string {
|
||||
const path = location.pathname
|
||||
const segments = path.split('/').filter(Boolean);
|
||||
const deviceIndex = segments.findIndex(segment => segment === 'devices');
|
||||
|
||||
if (deviceIndex === -1) {
|
||||
throw new Error('Path does not contain "devices"');
|
||||
}
|
||||
|
||||
// Build the new segments
|
||||
const newSegments = [
|
||||
...segments.slice(0, deviceIndex),
|
||||
'groups',
|
||||
groupID,
|
||||
...segments.slice(deviceIndex),
|
||||
deviceID
|
||||
];
|
||||
|
||||
return '/' + newSegments.join('/');
|
||||
}
|
||||
|
||||
// useEffect(() => {
|
||||
// console.log(devices)
|
||||
// }, [devices])
|
||||
|
||||
const toDevice = (device: Device) => {
|
||||
let url = prependToUrl(getGroup() ? "groups/" + getGroup().id() : "")
|
||||
url = url + "/" + device.id()
|
||||
navigate(url, { replace: true, state: {device: device} })
|
||||
let url = getGroup() ? insertGroupContext(getGroup().id().toString(), device.id().toString()) : ""
|
||||
if (url.length < 1) url = device.id().toString()
|
||||
navigate(url, { state: {device: device} })
|
||||
};
|
||||
|
||||
const handleTabChange = (_event: React.SyntheticEvent, newValue: string) => {
|
||||
|
|
@ -294,9 +368,10 @@ export default function 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 +384,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}>
|
||||
|
|
@ -331,12 +408,13 @@ export default function Devices() {
|
|||
title: "Description",
|
||||
render: (device: Device) => {
|
||||
const description = device.settings?.description ?? ""
|
||||
let size = (hasCo2 && !groupGas) ? 26 : 36
|
||||
return (
|
||||
<Box className={classes.descriptionCellContainer}>
|
||||
<Tooltip title={device.settings?.description}>
|
||||
<Box className={classes.descriptionCellContainer} width={size*6}>
|
||||
<Tooltip title={description}>
|
||||
<span>
|
||||
{description.length > 36
|
||||
? description.substring(0, 36) + "..."
|
||||
{description.length > size
|
||||
? description.substring(0, size) + "..."
|
||||
: description}
|
||||
</span>
|
||||
</Tooltip>
|
||||
|
|
@ -346,6 +424,7 @@ export default function Devices() {
|
|||
},
|
||||
{
|
||||
title: "Tags",
|
||||
sortKey: "tags",
|
||||
render: (device: Device) => {
|
||||
return (
|
||||
<Grid2 container spacing={1} className={classes.tagsCellContainer}>
|
||||
|
|
@ -362,6 +441,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,11 +461,101 @@ export default function Devices() {
|
|||
if (hasSen5x) {
|
||||
columns.push({
|
||||
title: "Sen5X",
|
||||
// sortKey: "hi",
|
||||
// disableSort: true,
|
||||
render: (device: Device) => {
|
||||
if (device.status.sen5x?.temperature) {
|
||||
return (
|
||||
<Box sx={{ marginLeft: 1 }}>
|
||||
<StatusSen5x device={device} />
|
||||
<StatusSen5x device={device} noDust={separateDust} />
|
||||
</Box>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<></>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
if (separateDust) columns.push({
|
||||
title: "Dust",
|
||||
render: (device: Device) => {
|
||||
if (device.status.sen5x?.temperature) {
|
||||
return (
|
||||
<Box sx={{ marginLeft: 1 }}>
|
||||
<StatusDust device={device} />
|
||||
</Box>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<></>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
if (hasCo) {
|
||||
columns.push({
|
||||
title: groupGas ? "Gas" : "CO",
|
||||
render: (device: Device) => {
|
||||
if (device.status.co) {
|
||||
return (
|
||||
<Box sx={{ marginLeft: 1 }}>
|
||||
<StatusGas device={device} gasType={groupGas ? "all" : "co"}/>
|
||||
</Box>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<></>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
if (hasCo2 && !groupGas) {
|
||||
columns.push({
|
||||
title: "CO2",
|
||||
render: (device: Device) => {
|
||||
if (device.status.co2) {
|
||||
return (
|
||||
<Box sx={{ marginLeft: 1 }}>
|
||||
<StatusGas device={device} gasType={"co2"}/>
|
||||
</Box>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<></>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
if (hasNo2 && !groupGas) {
|
||||
columns.push({
|
||||
title: "NO2",
|
||||
render: (device: Device) => {
|
||||
if (device.status.no2) {
|
||||
return (
|
||||
<Box sx={{ marginLeft: 1 }}>
|
||||
<StatusGas device={device} gasType={"no2"}/>
|
||||
</Box>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<></>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
if (hasO2 && !groupGas) {
|
||||
columns.push({
|
||||
title: "O2",
|
||||
render: (device: Device) => {
|
||||
if (device.status.o2) {
|
||||
return (
|
||||
<Box sx={{ marginLeft: 1 }}>
|
||||
<StatusGas device={device} gasType={"o2"}/>
|
||||
</Box>
|
||||
)
|
||||
} else {
|
||||
|
|
@ -487,6 +658,51 @@ export default function Devices() {
|
|||
)
|
||||
}
|
||||
|
||||
const preferencesButton = () => {
|
||||
return (
|
||||
<IconButton onClick={(event) => setPreferencesAnchor(event.currentTarget)}>
|
||||
<Tune />
|
||||
</IconButton>
|
||||
)
|
||||
}
|
||||
|
||||
const preferencesMenu = () => {
|
||||
return (
|
||||
<Menu
|
||||
id="userMenu"
|
||||
anchorEl={preferencesAnchor}
|
||||
open={preferencesAnchor !== null}
|
||||
onClose={() => setPreferencesAnchor(null)}
|
||||
disableAutoFocusItem>
|
||||
<MenuItem
|
||||
onClick={() => setSeparateDust(!separateDust)}
|
||||
sx={{ marginLeft: -0.75 }}
|
||||
dense
|
||||
>
|
||||
<ListItemIcon>
|
||||
<Checkbox
|
||||
checked={!separateDust}
|
||||
/>
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={"Group Dust"} />
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => setGroupGas(!groupGas)}
|
||||
sx={{ marginLeft: -0.75 }}
|
||||
dense
|
||||
>
|
||||
<ListItemIcon>
|
||||
<Checkbox
|
||||
checked={groupGas}
|
||||
|
||||
/>
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={"Group Gas"} />
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
)
|
||||
}
|
||||
|
||||
return(
|
||||
<PageContainer padding={isMobile ? 0 : 2}>
|
||||
<Box
|
||||
|
|
@ -532,6 +748,46 @@ 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}
|
||||
endTitleElement={preferencesButton}
|
||||
loadMore={()=>{
|
||||
let currentRows = cloneDeep(devices)
|
||||
deviceAPI.list(
|
||||
limit,
|
||||
currentRows.length,
|
||||
order,
|
||||
orderBy,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
getKeys(),
|
||||
getTypes(),
|
||||
fieldContains,
|
||||
search,
|
||||
as
|
||||
).then(resp => {
|
||||
let newDevices: Device[] = []
|
||||
resp.data.devices.forEach(device => {
|
||||
if (device.status?.plenum?.temperature) setHasPlenums(true)
|
||||
if (device.status?.sen5x?.temperature) setHasSen5x(true)
|
||||
if (device.status?.o2) setHasO2(true)
|
||||
if (device.status?.co) setHasCo(true)
|
||||
if (device.status?.co2) setHasCo2(true)
|
||||
if (device.status?.no2) setHasNo2(true)
|
||||
newDevices.push(Device.create(device))
|
||||
})
|
||||
setDevices(currentRows.concat(newDevices))
|
||||
}).catch(err => {
|
||||
|
||||
})
|
||||
}}
|
||||
/>
|
||||
<ProvisionDevice
|
||||
isOpen={isProvisionDialogOpen}
|
||||
|
|
@ -550,6 +806,7 @@ export default function Devices() {
|
|||
removeGroupCallback={removeGroupCallback}
|
||||
addGroupCallback={addGroupCallback}
|
||||
/>
|
||||
{preferencesMenu()}
|
||||
</PageContainer>
|
||||
)
|
||||
}
|
||||
|
|
@ -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")
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<UploadFirmware
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import { appendToUrl } from "navigation/Router";
|
|||
import { getDeviceStateHelper } from "pbHelpers/DeviceState";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { blue, green } from "@mui/material/colors";
|
||||
import { cloneDeep } from "lodash";
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) => {
|
||||
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)
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</PageContainer>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
})
|
||||
}}
|
||||
/>
|
||||
<GroupSettings
|
||||
// initialGroup={selectedGroup}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import DeviceLinkDrawer from "common/DeviceLinkDrawer";
|
|||
import ResponsiveTable, { Column } from "common/ResponsiveTable";
|
||||
//import { getTableIcons } from "common/ResponsiveTable";
|
||||
import { useMobile } from "hooks";
|
||||
import { cloneDeep } from "lodash";
|
||||
//import MaterialTable from "material-table";
|
||||
import { Device } from "models";
|
||||
import { ObjectHeater } from "models/ObjectHeater";
|
||||
|
|
@ -109,17 +110,19 @@ export default function Heaters() {
|
|||
setPage={(page)=>{setTablePage(page)}}
|
||||
handleRowsPerPageChange={handleChange}
|
||||
onRowClick={goToHeater}
|
||||
/>
|
||||
)
|
||||
};
|
||||
|
||||
const mobileList = () => {
|
||||
return (
|
||||
<List>
|
||||
{heaters.map(h => (
|
||||
<ObjectHeaterCard
|
||||
key={h.heater?.key}
|
||||
heaterData={h}
|
||||
loadMore={()=>{
|
||||
let current = cloneDeep(heaters)
|
||||
objectHeaterAPI
|
||||
.listObjectHeatersPageData(pageSize, current.length, undefined, undefined, searchText, as)
|
||||
.then(resp => {
|
||||
setHeaters(heaters.concat(resp.data.heaterData));
|
||||
});
|
||||
}}
|
||||
renderMobile={(row)=>{
|
||||
return (
|
||||
<ObjectHeaterCard
|
||||
key={row.heater?.key}
|
||||
heaterData={row}
|
||||
linkDevice={(heater, linkedDevices) => {
|
||||
setHeaterToLink(heater);
|
||||
setOpenLinkDrawer(true);
|
||||
|
|
@ -131,9 +134,10 @@ export default function Heaters() {
|
|||
setDevices(d);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</List>
|
||||
);
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
@ -180,7 +184,7 @@ export default function Heaters() {
|
|||
}}
|
||||
pulse={heaters.length < 1}
|
||||
/>
|
||||
{isMobile ? mobileList() : desktopTable()}
|
||||
{desktopTable()}
|
||||
</PageContainer>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<AddMine open={mineDialog} closeCallback={closeCallback} refreshCallback={load} />
|
||||
</PageContainer>
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<React.Fragment>
|
||||
|
|
@ -230,7 +258,7 @@ export default function Sites() {
|
|||
siteCards.push(
|
||||
<Card
|
||||
key={site.site.key()}
|
||||
style={{ margin: 10, padding: 5 }}
|
||||
style={{ margin: 10, padding: 5 }}
|
||||
onClick={() => {
|
||||
goToSite(site);
|
||||
}}>
|
||||
|
|
@ -297,6 +325,9 @@ export default function Sites() {
|
|||
return siteCards;
|
||||
})}
|
||||
</TabPanelMine>
|
||||
{sites.length < total &&
|
||||
<Button onClick={loadMore} variant="contained" color="primary" fullWidth>Load More</Button>
|
||||
}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue