added heaters page (still need to fix table) fixed the heater card on the site page

This commit is contained in:
csawatzky 2025-04-03 15:19:16 -06:00
parent 5065c07820
commit 43226717c1
7 changed files with 808 additions and 45 deletions

2
package-lock.json generated
View file

@ -10770,7 +10770,7 @@
}, },
"node_modules/protobuf-ts": { "node_modules/protobuf-ts": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#d1cf77df1de7c39e0cdf6663c7a25a364935f242", "resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#43253de7ee8c4d3b55b4b2cbbce41d782222fcfe",
"dependencies": { "dependencies": {
"protobufjs": "^6.8.8" "protobufjs": "^6.8.8"
} }

View file

@ -15,6 +15,7 @@ import AviationMap from "pages/AviationMap";
import ConstructionSiteMap from "pages/ConstructionSiteMap"; import ConstructionSiteMap from "pages/ConstructionSiteMap";
import Sites from "pages/Sites"; import Sites from "pages/Sites";
import Site from "pages/Site"; import Site from "pages/Site";
import Heaters from "pages/Heaters";
//import Site from "pages/Site"; //import Site from "pages/Site";
const DeviceHistory = lazy(() => import("pages/DeviceHistory")); const DeviceHistory = lazy(() => import("pages/DeviceHistory"));
@ -55,6 +56,7 @@ export default function Router(props: Props) {
<Route path="mines/*" element={<MinesRoute/>} /> <Route path="mines/*" element={<MinesRoute/>} />
<Route path="terminals/*" element={<TerminalGatesRoute />} /> <Route path="terminals/*" element={<TerminalGatesRoute />} />
<Route path="jobsites/*" element={<JobsitesRoute />} /> <Route path="jobsites/*" element={<JobsitesRoute />} />
<Route path="heaters/*" element={<ObjectHeatersRoute />} />
</Routes> </Routes>
) )
} }
@ -204,6 +206,28 @@ export default function Router(props: Props) {
); );
}; };
const ObjectHeatersRoute = () => {
return (
<div>
<Routes>
<Route
key="Heaters page route"
path="" // "/settings/basic"
element={<Heaters key={"Heaters page"} />}
/>
{/* <Route
path="/:heaterKey" // "/settings/basic"
element={<Site />}
/>
<Route
path="/:heaterKey/*" // "/settings/basic"
element={<RelativeRoutes />}
/> */}
</Routes>
</div>
);
};
const GroupsRoute = () => { const GroupsRoute = () => {
return ( return (

View file

@ -38,6 +38,7 @@ import PlaneIcon from "products/AviationIcons/PlaneIcon";
import AirportMapIcon from "products/AviationIcons/AirportMapIcon"; import AirportMapIcon from "products/AviationIcons/AirportMapIcon";
import JobsiteIcon from "products/Construction/JobSiteIcon"; import JobsiteIcon from "products/Construction/JobSiteIcon";
import { getThemeType } from "theme"; import { getThemeType } from "theme";
import ObjectHeaterIcon from "products/Construction/ObjectHeaterIcon";
const drawerWidth = 230; const drawerWidth = 230;
@ -281,6 +282,20 @@ export default function SideNavigator(props: Props) {
</ListItemButton> </ListItemButton>
</Tooltip> </Tooltip>
)} )}
{(isAdCon || user.hasFeature("admin")) && (
<Tooltip title="Heaters" placement="right">
<ListItemButton
id="tour-heaters"
onClick={() => goTo("/heaters")}
classes={getClasses("/heaters")}
>
<ListItemIcon>
<ObjectHeaterIcon />
</ListItemIcon>
{open && <ListItemText primary="Heaters" />}
</ListItemButton>
</Tooltip>
)}
</List> </List>
) )
} }

View file

@ -0,0 +1,58 @@
import { Fab, Theme } from "@mui/material";
import { makeStyles } from "@mui/styles";
import Icon from "assets/components/heaterDark.png";
import { ImgIcon } from "common/ImgIcon";
import { useMobile } from "hooks";
interface Props {
onClick: () => void;
pulse: boolean;
}
const useStyles = makeStyles((theme: Theme) => ({
"@keyframes pulsate": {
to: {
boxShadow: "0 0 0 16px" + theme.palette.primary.main + "00"
}
},
fab: {
zIndex: 2,
background: theme.palette.primary.main,
"&:hover": {
background: theme.palette.primary.main
},
"&:focus": {
background: theme.palette.primary.main
},
position: "fixed",
bottom: theme.spacing(8), //for mobile navigator
right: theme.spacing(2),
[theme.breakpoints.up("sm")]: {
bottom: theme.spacing(2)
}
},
pulse: {
zIndex: 2,
boxShadow: "0 0 0 0 " + theme.palette.primary.main + "75",
animation: "$pulsate 1.75s infinite cubic-bezier(0.66, 0.33, 0, 1)"
}
}));
export default function AddObjectHeaterFab(props: Props) {
const { onClick, pulse } = props;
const classes = useStyles();
const isMobile = useMobile();
const pulseString = pulse ? classes.pulse : "";
const classString = classes.fab + " " + pulseString;
return (
<Fab
onClick={onClick}
aria-label="Add New Heater"
className={classString}
size={isMobile ? "medium" : "large"}>
<ImgIcon alt="Add New Heater" src={Icon} />
</Fab>
);
}

View file

@ -9,7 +9,7 @@ import {
ListItemText, ListItemText,
Theme, Theme,
Typography, Typography,
useTheme //useTheme
} from "@mui/material"; } from "@mui/material";
import { GetDefaultDateRange } from "common/time/DateRange"; import { GetDefaultDateRange } from "common/time/DateRange";
import UnitMeasurementSummary from "component/UnitMeasurementSummary"; import UnitMeasurementSummary from "component/UnitMeasurementSummary";
@ -79,7 +79,7 @@ export default function ObjectHeaterCard(props: Props) {
const [allComponentsRecent, setAllComponentsRecent] = useState(true); const [allComponentsRecent, setAllComponentsRecent] = useState(true);
//const history = useHistory(); //const history = useHistory();
const navigate = useNavigate(); const navigate = useNavigate();
const theme = useTheme(); //const theme = useTheme();
useEffect(() => { useEffect(() => {
let heater = ObjectHeater.any(heaterData.heater); let heater = ObjectHeater.any(heaterData.heater);
@ -237,7 +237,7 @@ export default function ObjectHeaterCard(props: Props) {
if (!lastFuelMutation) { if (!lastFuelMutation) {
return ( return (
<Grid container direction="row" wrap="nowrap" alignContent="center" alignItems="center"> <Grid container direction="row" wrap="nowrap" alignContent="center" alignItems="center">
{/* <Grid item> <Grid>
<Avatar <Avatar
variant="square" variant="square"
alt="icon" alt="icon"
@ -245,7 +245,7 @@ export default function ObjectHeaterCard(props: Props) {
style={{ float: "left" }} style={{ float: "left" }}
className={classes.avatarIcon} className={classes.avatarIcon}
/> />
</Grid> */} </Grid>
<Grid style={{ paddingLeft: 5 }}> <Grid style={{ paddingLeft: 5 }}>
N/A N/A
</Grid> </Grid>
@ -295,42 +295,11 @@ export default function ObjectHeaterCard(props: Props) {
); );
}; };
//content of the card of there is no device linked to the heater
const unlinkedCard = () => {
return (
<React.Fragment>
<CardContent style={{ paddingLeft: 30, paddingRight: 30 }}>
<Grid
container
direction="row"
alignContent="center"
alignItems="center">
<Grid size={4}>
<EventBlocker>
<Button
variant="contained"
onClick={() => {
linkDevice(heater, heaterData.devices);
}}>
<Link />
</Button>
</EventBlocker>
</Grid>
<Grid size={7}>
<Typography>{heater.name}</Typography>
</Grid>
<Grid size={1}>
<ObjectHeaterIcon />
</Grid>
</Grid>
</CardContent>
</React.Fragment>
);
};
//content of the card if a device is linked to the heater //content of the card if a device is linked to the heater
const linkedCard = (device: Device) => { const cardBody = (device?: Device) => {
const productIcon = GetDeviceProductIcon(device); const productIcon = GetDeviceProductIcon(device ?? Device.create());
if(device){
return ( return (
<React.Fragment> <React.Fragment>
<Box height="100%" display="flex" alignItems="center" flexWrap="nowrap" padding={2}> <Box height="100%" display="flex" alignItems="center" flexWrap="nowrap" padding={2}>
@ -382,10 +351,10 @@ export default function ObjectHeaterCard(props: Props) {
direction="row" direction="row"
alignContent="center" alignContent="center"
alignItems="center"> alignItems="center">
<Grid>{componentDisplay(heaterComponent)}</Grid> <Grid size={3}>{componentDisplay(heaterComponent)}</Grid>
<Grid>{componentDisplay(heaterTempComponent)}</Grid> <Grid size={3}>{componentDisplay(heaterTempComponent)}</Grid>
<Grid>{componentDisplay(siteTempComponent)}</Grid> <Grid size={3}>{componentDisplay(siteTempComponent)}</Grid>
<Grid>{fuelMutationDisplay()}</Grid> <Grid size={3}>{fuelMutationDisplay()}</Grid>
</Grid> </Grid>
</CardContent> </CardContent>
) : ( ) : (
@ -399,11 +368,42 @@ export default function ObjectHeaterCard(props: Props) {
)} )}
</React.Fragment> </React.Fragment>
); );
}else {
return (
<React.Fragment>
<CardContent style={{ paddingLeft: 30, paddingRight: 30 }}>
<Grid
container
direction="row"
alignContent="center"
alignItems="center">
<Grid size={4}>
<EventBlocker>
<Button
variant="contained"
onClick={() => {
linkDevice(heater, heaterData.devices);
}}>
<Link />
</Button>
</EventBlocker>
</Grid>
<Grid size={7}>
<Typography>{heater.name}</Typography>
</Grid>
<Grid size={1}>
<ObjectHeaterIcon />
</Grid>
</Grid>
</CardContent>
</React.Fragment>
)
}
}; };
return ( return (
<Card raised onClick={goToHeater} style={{ margin: 5 }}> <Card raised onClick={goToHeater} style={{ margin: 5 }}>
{connectedDevice ? linkedCard(connectedDevice) : unlinkedCard()} {cardBody(connectedDevice)}
</Card> </Card>
); );
} }

View file

@ -0,0 +1,464 @@
import {
Button,
DialogActions,
DialogContent,
DialogTitle,
Grid2 as Grid,
InputAdornment,
List,
ListItem,
MenuItem,
Tab,
Tabs,
TextField,
Tooltip,
Typography
} from "@mui/material";
import ResponsiveDialog from "common/ResponsiveDialog";
import { Component, Site } from "models";
import { ObjectHeater } from "models/ObjectHeater";
import { useGlobalState, useObjectHeaterAPI, useSiteAPI } from "providers";
import { useSnackbar } from "hooks";
import React, { useEffect, useState } from "react";
import { pond } from "protobuf-ts/pond";
import LinearMutationBuilder from "common/LinearMutationBuilder";
import { makeStyles } from "@mui/styles";
interface TabPanelProps {
children?: React.ReactNode;
index: any;
value: any;
}
function TabPanelMine(props: TabPanelProps) {
const { children, value, index, ...other } = props;
return (
<div
role="tabpanel"
hidden={value !== index}
aria-labelledby={`simple-tab-${index}`}
{...other}>
{value === index && <React.Fragment>{children}</React.Fragment>}
</div>
);
}
const useStyles = makeStyles(() => ({
dialogContent: {
minHeight: "25vh"
},
tabSmall: {
width: "100%",
boxSizing: "border-box",
flexShrink: 1,
minWidth: "48px",
marginTop: 0,
paddingTop: 0
}
})
);
interface Props {
open: boolean;
componentsByDevice?: Map<string, Component[]>;
close: (heater?: ObjectHeater) => void;
heater?: ObjectHeater;
sites?: Site[];
}
export default function ObjectHeaterSettings(props: Props) {
const { open, close, heater, sites, componentsByDevice } = props;
const objectHeaterAPI = useObjectHeaterAPI();
const siteAPI = useSiteAPI();
const [{ as }] = useGlobalState();
const classes = useStyles();
const [siteOptions, setSiteOptions] = useState<Site[]>([]);
const [heaterName, setHeaterName] = useState("");
const [heaterMake, setHeaterMake] = useState("");
const [heaterModel, setHeaterModel] = useState("");
const [siteKey, setSiteKey] = useState("");
const [fuelType, setFuelType] = useState<pond.FuelType>(pond.FuelType.FUEL_TYPE_UNKNOWN);
const [tankSize, setTankSize] = useState(0);
const [fuelConsumption, setFuelConsumption] = useState(0);
const [airCirculation, setAirCirculation] = useState(0);
const { openSnack } = useSnackbar();
const [currentTab, setCurrentTab] = useState(0);
const [linearMutations, setLinearMutations] = useState<pond.LinearMutation[]>([]);
const [newMutationDialog, setNewMutationDialog] = useState(false);
const [existingMutation, setExistingMutation] = useState<pond.LinearMutation>();
const [removeDialog, setRemoveDialog] = useState(false);
const closeDialog = (heater?: ObjectHeater) => {
close(heater);
};
//load the sites if not passed in
useEffect(() => {
if (sites) {
setSiteOptions(sites);
} else {
siteAPI.listSites(0, 0, undefined, undefined, undefined, as).then(resp => {
setSiteOptions(resp.data.sites.map(s => Site.any(s)));
});
}
}, [sites, as, siteAPI]);
//set the state variables
useEffect(() => {
setHeaterName("");
setHeaterMake("");
setHeaterModel("");
setSiteKey("");
setFuelType(pond.FuelType.FUEL_TYPE_UNKNOWN);
setFuelConsumption(0);
setTankSize(0);
setAirCirculation(0);
if (heater) {
setHeaterName(heater.name);
setHeaterMake(heater.settings.make);
setHeaterModel(heater.settings.model);
setSiteKey(heater.settings.siteKey);
setFuelType(heater.settings.fuelType);
setFuelConsumption(heater.settings.fuelConsumption);
setTankSize(heater.settings.tankSize);
setAirCirculation(heater.settings.airCirculation);
setLinearMutations(heater.settings.mutations);
}
}, [heater]);
const confirm = () => {
if (heater) {
let settings = heater.settings;
settings.make = heaterMake;
settings.model = heaterModel;
settings.siteKey = siteKey;
settings.tankSize = tankSize;
settings.fuelType = fuelType;
settings.fuelConsumption = fuelConsumption;
settings.airCirculation = airCirculation;
settings.mutations = linearMutations;
objectHeaterAPI
.updateObjectHeater(heater.key, heaterName, settings)
.then(resp => {
openSnack("Heater update");
let updatedHeater = heater;
updatedHeater.name = heaterName;
updatedHeater.settings = settings;
closeDialog(updatedHeater);
})
.catch(err => {
openSnack("Failed to update heater");
});
} else {
let settings: pond.ObjectHeaterSettings = pond.ObjectHeaterSettings.create({
make: heaterMake,
model: heaterModel,
siteKey: siteKey,
fuelType: fuelType,
tankSize: tankSize,
fuelConsumption: fuelConsumption,
airCirculation: airCirculation,
mutations: linearMutations
});
objectHeaterAPI
.addObjectHeater(heaterName, settings)
.then(resp => {
openSnack("New heater added");
let newHeater = ObjectHeater.create(
pond.ObjectHeater.create({ key: resp.data.key, name: heaterName, settings: settings })
);
closeDialog(newHeater);
})
.catch(err => {
openSnack("There was a problem adding your heater");
});
}
};
const removeHeater = () => {
if (heater) {
objectHeaterAPI
.removeObjectHeater(heater.key)
.then(resp => {
openSnack("Heater Removed");
setRemoveDialog(false);
closeDialog();
})
.catch(err => {
openSnack("Failed to remove heater");
});
}
};
const settingsContent = () => {
return (
<React.Fragment>
<TextField
label="Heater Name"
fullWidth
value={heaterName}
onChange={e => {
setHeaterName(e.target.value);
}}
/>
<TextField
label="Site"
fullWidth
select
value={siteKey}
onChange={e => {
setSiteKey(e.target.value);
}}>
{siteOptions.map(site => (
<MenuItem key={site.key()} value={site.key()}>
{site.siteName()}
</MenuItem>
))}
</TextField>
<TextField
label="Make"
fullWidth
value={heaterMake}
onChange={e => {
setHeaterMake(e.target.value);
}}
/>
<TextField
label="Model"
fullWidth
value={heaterModel}
onChange={e => {
setHeaterModel(e.target.value);
}}
/>
<TextField
label="Fuel Type"
fullWidth
select
value={fuelType}
onChange={e => {
setFuelType(+e.target.value as pond.FuelType);
}}>
<MenuItem key="unknown" value={pond.FuelType.FUEL_TYPE_UNKNOWN}>
Select Fuel Type
</MenuItem>
<MenuItem key="gas" value={pond.FuelType.FUEL_TYPE_GASOLINE}>
Gasoline
</MenuItem>
<MenuItem key="diesel" value={pond.FuelType.FUEL_TYPE_DIESEL}>
Diesel
</MenuItem>
<MenuItem key="propane" value={pond.FuelType.FUEL_TYPE_PROPANE}>
Propane
</MenuItem>
</TextField>
<TextField
label="Tank Size"
fullWidth
type="number"
value={tankSize}
onChange={e => {
setTankSize(+e.target.value);
}}
InputProps={{
endAdornment: <InputAdornment position="end">G</InputAdornment>
}}
/>
<TextField
label="Fuel Consumption"
fullWidth
type="number"
value={fuelConsumption}
onChange={e => {
setFuelConsumption(+e.target.value);
}}
InputProps={{
endAdornment: <InputAdornment position="end">G/hr</InputAdornment>
}}
/>
<TextField
label="Air Circulation"
fullWidth
type="number"
value={airCirculation}
onChange={e => {
setAirCirculation(+e.target.value);
}}
InputProps={{
endAdornment: <InputAdornment position="end">CFM</InputAdornment>
}}
/>
</React.Fragment>
);
};
const mutationsContent = () => {
let mutations: JSX.Element[] = [];
if (heater && heater.settings.mutations) {
heater.settings.mutations.forEach((mut, i) => {
mutations.push(
<ListItem key={"mutation" + i}>
<Grid
container
direction="row"
alignContent="center"
alignItems="center">
<Grid size={5}>
<Typography>{mut.mutationName}</Typography>
</Grid>
<Grid>
<Button
variant="contained"
style={{ backgroundColor: "red" }}
onClick={() => {
let lm = linearMutations;
lm.splice(i, 1);
setLinearMutations([...lm]);
}}>
Remove
</Button>
<Button
style={{ marginLeft: 10 }}
variant="contained"
color="primary"
onClick={() => {
setExistingMutation(mut);
setNewMutationDialog(true);
}}>
Edit
</Button>
</Grid>
</Grid>
</ListItem>
);
});
}
let mutationList: JSX.Element = <List>{mutations}</List>;
return (
<React.Fragment>
<Button
variant="contained"
color="primary"
onClick={() => {
setExistingMutation(undefined);
setNewMutationDialog(true);
}}>
Add New Mutation
</Button>
<List>{mutationList}</List>
</React.Fragment>
);
};
const removeConfirmation = () => {
return (
<ResponsiveDialog open={removeDialog} onClose={() => setRemoveDialog(false)}>
<DialogContent>Are you sure you would like to delete heater?</DialogContent>
<DialogActions>
<Button
variant="contained"
color="primary"
onClick={() => {
setRemoveDialog(false);
}}>
Cancel
</Button>
<Button
variant="contained"
color="primary"
onClick={() => {
removeHeater();
}}>
Delete
</Button>
</DialogActions>
</ResponsiveDialog>
);
};
return (
<React.Fragment>
{removeConfirmation()}
<ResponsiveDialog
open={open}
onClose={() => {
closeDialog();
}}>
<DialogTitle>Heater Settings</DialogTitle>
<DialogContent>
<Tabs
value={currentTab}
onChange={(_, value) => setCurrentTab(value)}
indicatorColor="primary"
textColor="primary"
variant="fullWidth"
aria-label="device tabs"
classes={{ root: classes.tabSmall }}>
<Tab label={"Heater Settings"} className={classes.tabSmall} />
<Tab
style={{ pointerEvents: "auto" }}
label={
componentsByDevice ? (
"Calculated Measurements"
) : (
<Tooltip title="Requires Connected Device">
<span>Calculated Measurements</span>
</Tooltip>
)
}
className={classes.tabSmall}
disabled={!componentsByDevice}
/>
</Tabs>
<TabPanelMine value={currentTab} index={0}>
{settingsContent()}
</TabPanelMine>
<TabPanelMine value={currentTab} index={1}>
{mutationsContent()}
</TabPanelMine>
</DialogContent>
<DialogActions>
<Grid container direction="row">
<Grid>
{heater && (
<Button
variant="contained"
style={{ backgroundColor: "red" }}
onClick={() => {
setRemoveDialog(true);
}}>
Delete
</Button>
)}
</Grid>
<Grid></Grid>
</Grid>
<Button
variant="contained"
disabled={heaterName === ""}
color="primary"
onClick={() => {
confirm();
}}>
{heater ? "Update" : "Create"}
</Button>
</DialogActions>
{componentsByDevice && (
<LinearMutationBuilder
existingMutation={existingMutation}
componentsByDevice={componentsByDevice}
open={newMutationDialog}
onClose={() => {
setNewMutationDialog(false);
}}
onSubmit={newMutation => {
let lm = linearMutations;
lm.push(newMutation);
setLinearMutations(lm);
}}
/>
)}
</ResponsiveDialog>
</React.Fragment>
);
}

202
src/pages/Heaters.tsx Normal file
View file

@ -0,0 +1,202 @@
import { List } from "@mui/material";
import DeviceLinkDrawer from "common/DeviceLinkDrawer";
import ResponsiveTable from "common/ResponsiveTable";
//import { getTableIcons } from "common/ResponsiveTable";
import { useMobile } from "hooks";
//import MaterialTable from "material-table";
import { Device } from "models";
import { ObjectHeater } from "models/ObjectHeater";
import AddObjectHeaterFab from "objectHeater/AddObjectHeaterFab";
import ObjectHeaterCard from "objectHeater/ObjectHeaterCard";
import ObjectHeaterSettings from "objectHeater/ObjectHeaterSettings";
import { pond } from "protobuf-ts/pond";
import { useGlobalState, useObjectHeaterAPI } from "providers";
import { useCallback, useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
//import { useHistory } from "react-router";
import PageContainer from "./PageContainer";
export default function Heaters() {
const [heaterDialog, setHeaterDialog] = useState(false);
const objectHeaterAPI = useObjectHeaterAPI();
const [{ as }] = useGlobalState();
//const history = useHistory();
const navigate = useNavigate();
const [heaters, setHeaters] = useState<pond.ObjectHeaterData[]>([]);
const [openLinkDrawer, setOpenLinkDrawer] = useState(false);
const [devices, setDevices] = useState<Map<string, pond.ComprehensiveDevice>>(
new Map<string, pond.ComprehensiveDevice>()
);
const [heaterToLink, setHeaterToLink] = useState<ObjectHeater>();
const isMobile = useMobile();
const [tablePage, setTablePage] = useState(0)
const [pageSize, setPageSize] = useState(10)
const [total, setTotal] = useState(0)
const loadData = useCallback(() => {
objectHeaterAPI
.listObjectHeatersPageData(500, 0, undefined, undefined, undefined, as)
.then(resp => {
setTotal(resp.data.total)
setHeaters(resp.data.heaterData);
});
}, [objectHeaterAPI, as]);
useEffect(() => {
loadData();
}, [loadData]);
const goToHeater = (heater: ObjectHeater, devices: pond.ComprehensiveDevice[]) => {
let path = "/objectHeaters/" + heater.key;
navigate(path, {state: { heater: heater, devices: devices}})
};
const desktopTable = () => {
// return (
// <MaterialTable
// columns={[
// {
// title: "Heater",
// field: "heater.name",
// headerStyle: {
// fontWeight: 650,
// fontSize: 20
// }
// },
// {
// title: "Site",
// field: "site.settings.siteName",
// headerStyle: {
// fontWeight: 650,
// fontSize: 20
// }
// },
// {
// title: "Make",
// field: "heater.settings.make",
// headerStyle: {
// fontWeight: 650,
// fontSize: 20
// }
// },
// {
// title: "Model",
// field: "heater.settings.model",
// headerStyle: {
// fontWeight: 650,
// fontSize: 20
// }
// },
// {
// title: "Device",
// headerStyle: {
// fontWeight: 650,
// fontSize: 20
// },
// render: rowData => {
// if (rowData.devices.length > 0) {
// if (rowData.devices[0].device?.settings?.name) {
// return rowData.devices[0].device?.settings?.name;
// } else {
// return rowData.devices[0].device?.settings?.deviceId;
// }
// } else {
// return "No Device Found";
// }
// }
// }
// ]}
// data={heaters}
// icons={getTableIcons()}
// onRowClick={(_, heaterData) => {
// heaterData && goToHeater(ObjectHeater.any(heaterData.heater), heaterData.devices);
// }}
// title={"Heaters"}
// options={{
// pageSize: 10
// }}
// />
// );
return (
<ResponsiveTable
page={tablePage}
pageSize={pageSize}
total={total}
rows={heaters}
setPage={(page)=>{setTablePage(page)}}
handleRowsPerPageChange={()=>{}}
/>
)
};
const mobileList = () => {
return (
<List>
{heaters.map(h => (
<ObjectHeaterCard
key={h.heater?.key}
heaterData={h}
linkDevice={(heater, linkedDevices) => {
setHeaterToLink(heater);
setOpenLinkDrawer(true);
let d = new Map<string, pond.ComprehensiveDevice>();
linkedDevices.forEach(compDev => {
let device = Device.any(compDev.device);
d.set(device.id().toString(), compDev);
});
setDevices(d);
}}
/>
))}
</List>
);
};
return (
<PageContainer>
{isMobile && (
<DeviceLinkDrawer
open={openLinkDrawer}
close={() => {
setOpenLinkDrawer(false);
}}
linkedDevices={devices}
updateLinkedDevices={(dev, linked) => {
let deviceID = dev.device?.settings?.deviceId;
if (deviceID && heaterToLink) {
if (linked) {
objectHeaterAPI
.updateLink(heaterToLink.key, "objectHeater", deviceID.toString(), "device", [
"read",
"write",
"grant",
"revoke"
])
.then(resp => {
if (deviceID) {
devices.set(deviceID.toString(), dev);
setOpenLinkDrawer(false);
loadData();
}
});
}
}
}}
/>
)}
<ObjectHeaterSettings
open={heaterDialog}
close={() => {
setHeaterDialog(false);
}}
/>
<AddObjectHeaterFab
onClick={() => {
setHeaterDialog(true);
}}
pulse={heaters.length < 1}
/>
{isMobile ? mobileList() : desktopTable()}
</PageContainer>
);
}