added heaters page (still need to fix table) fixed the heater card on the site page
This commit is contained in:
parent
5065c07820
commit
43226717c1
7 changed files with 808 additions and 45 deletions
2
package-lock.json
generated
2
package-lock.json
generated
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 (
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
58
src/objectHeater/AddObjectHeaterFab.tsx
Normal file
58
src/objectHeater/AddObjectHeaterFab.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
464
src/objectHeater/ObjectHeaterSettings.tsx
Normal file
464
src/objectHeater/ObjectHeaterSettings.tsx
Normal 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
202
src/pages/Heaters.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue