bins page now has the add bin button
This commit is contained in:
parent
429f990b41
commit
cd972409b2
15 changed files with 134331 additions and 7 deletions
59
src/bin/AddBinFab.tsx
Normal file
59
src/bin/AddBinFab.tsx
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
import { Fab, Theme } from "@mui/material";
|
||||||
|
import { makeStyles } from "@mui/styles";
|
||||||
|
import AddBinIcon from "assets/products/bindapt/addBin.png";
|
||||||
|
import { ImgIcon } from "common/ImgIcon";
|
||||||
|
import { useMobile } from "hooks";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
onClick: () => void;
|
||||||
|
pulse: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) => {
|
||||||
|
const yellow = "#ffea00";
|
||||||
|
return ({
|
||||||
|
"@keyframes pulsate": {
|
||||||
|
to: {
|
||||||
|
boxShadow: "0 0 0 16px" + yellow + "00"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fab: {
|
||||||
|
background: yellow,
|
||||||
|
"&:hover": {
|
||||||
|
background: yellow
|
||||||
|
},
|
||||||
|
"&:focus": {
|
||||||
|
background: yellow
|
||||||
|
},
|
||||||
|
position: "fixed",
|
||||||
|
bottom: theme.spacing(8), //for mobile navigator
|
||||||
|
right: theme.spacing(2),
|
||||||
|
[theme.breakpoints.up("sm")]: {
|
||||||
|
bottom: theme.spacing(2)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
pulse: {
|
||||||
|
boxShadow: "0 0 0 0 " + yellow + "75",
|
||||||
|
animation: "$pulsate 1.75s infinite cubic-bezier(0.66, 0.33, 0, 1)"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function AddBinFab(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="Create Bin"
|
||||||
|
className={classString}
|
||||||
|
size={isMobile ? "medium" : "large"}>
|
||||||
|
<ImgIcon alt="Create Bin" src={AddBinIcon} />
|
||||||
|
</Fab>
|
||||||
|
);
|
||||||
|
}
|
||||||
154
src/bin/BinSelector.tsx
Normal file
154
src/bin/BinSelector.tsx
Normal file
|
|
@ -0,0 +1,154 @@
|
||||||
|
import { Grid2 as Grid, TextField } from "@mui/material";
|
||||||
|
import { ImportBins, jsonBin } from "common/DataImports/BinCables/BinCableImporter";
|
||||||
|
import SearchSelect, { Option } from "common/SearchSelect";
|
||||||
|
import { useMobile } from "hooks";
|
||||||
|
import { pond } from "protobuf-ts/pond";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { getDistanceUnit } from "utils";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
optionsChanged: (binOptions: jsonBin[]) => void;
|
||||||
|
vertical?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function BinSelector(props: Props) {
|
||||||
|
const { optionsChanged, vertical } = props;
|
||||||
|
const isMobile = useMobile();
|
||||||
|
const [manufacturerOptions, SetManufacturerOptions] = useState<Option[]>([]);
|
||||||
|
const [manufacturer, SetManufacturer] = useState<Option | null>(null);
|
||||||
|
const [binStyle, setBinStyle] = useState<Option | null>(null); //flat bottom or hopper
|
||||||
|
const [minDiameter, setMinDiameter] = useState(0);
|
||||||
|
const [maxDiameter, setMaxDiameter] = useState(0);
|
||||||
|
|
||||||
|
//set the initial manufacturer options
|
||||||
|
useEffect(() => {
|
||||||
|
let stringOps: string[] = [];
|
||||||
|
let options: Option[] = [];
|
||||||
|
ImportBins().forEach((bin: jsonBin) => {
|
||||||
|
if (!stringOps.includes(bin.Manufacturer)) {
|
||||||
|
stringOps.push(bin.Manufacturer);
|
||||||
|
options.push({
|
||||||
|
label: bin.Manufacturer,
|
||||||
|
value: bin.Manufacturer
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
SetManufacturerOptions(options);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
//start out with all the bins
|
||||||
|
let ops: jsonBin[] = [];
|
||||||
|
ImportBins().forEach((bin: jsonBin) => {
|
||||||
|
let addBin = true;
|
||||||
|
if (manufacturer) {
|
||||||
|
if (manufacturer.value !== bin.Manufacturer) {
|
||||||
|
addBin = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (binStyle) {
|
||||||
|
if (binStyle.value !== bin.Bottom) {
|
||||||
|
addBin = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (minDiameter !== 0) {
|
||||||
|
if (minDiameter > bin.Diameter) {
|
||||||
|
addBin = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (maxDiameter !== 0) {
|
||||||
|
if (maxDiameter < bin.Diameter) {
|
||||||
|
addBin = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (addBin) {
|
||||||
|
ops.push(bin);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
optionsChanged(ops);
|
||||||
|
}, [manufacturer, binStyle, minDiameter, maxDiameter]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
|
const binSelection = () => {
|
||||||
|
return (
|
||||||
|
<Grid container direction="row" spacing={2} alignContent="center" alignItems="center">
|
||||||
|
<Grid size={{ xs: isMobile || vertical ? 12 : 3 }}>
|
||||||
|
{/* manufacturer */}
|
||||||
|
<SearchSelect
|
||||||
|
selected={manufacturer}
|
||||||
|
changeSelection={(option: Option | null) => {
|
||||||
|
SetManufacturer(option);
|
||||||
|
}}
|
||||||
|
options={manufacturerOptions}
|
||||||
|
label="Bin Manufacturer"
|
||||||
|
loading={false}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid size={{ xs: isMobile || vertical ? 12 : 3 }}>
|
||||||
|
{/* binStyle */}
|
||||||
|
<SearchSelect
|
||||||
|
selected={binStyle}
|
||||||
|
changeSelection={(option: Option | null) => {
|
||||||
|
setBinStyle(option);
|
||||||
|
}}
|
||||||
|
options={[
|
||||||
|
{ label: "Flat", value: "Flat Bottom" },
|
||||||
|
{ label: "Hopper", value: "Hopper Bottom" }
|
||||||
|
]}
|
||||||
|
label="BinStyle"
|
||||||
|
loading={false}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
{/* bin use: at the moment all bins are farm/personal use so this is not needed yet */}
|
||||||
|
{/* <Grid item xs={3}>
|
||||||
|
<SearchSelect
|
||||||
|
selected={binUse}
|
||||||
|
changeSelection={(option: Option | null) => {
|
||||||
|
setBinUse(option);
|
||||||
|
}}
|
||||||
|
options={[
|
||||||
|
{label: "Farm", value: "Farm"},
|
||||||
|
{label: "Commercial", value: "Commercial"}
|
||||||
|
]}
|
||||||
|
label="BinStyle"
|
||||||
|
loading={false}
|
||||||
|
/>
|
||||||
|
</Grid> */}
|
||||||
|
<Grid size={{ xs: isMobile || vertical ? 12 : 3 }}>
|
||||||
|
{/* minD */}
|
||||||
|
<TextField
|
||||||
|
variant="outlined"
|
||||||
|
type="number"
|
||||||
|
value={minDiameter}
|
||||||
|
onChange={e => {
|
||||||
|
if (!isNaN(+e.target.value)) {
|
||||||
|
setMinDiameter(+e.target.value);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
label={
|
||||||
|
"Minimum Diameter " +
|
||||||
|
(getDistanceUnit() === pond.DistanceUnit.DISTANCE_UNIT_FEET ? "(ft)" : "(m)")
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid size={{ xs: isMobile || vertical ? 12 : 3 }}>
|
||||||
|
{/* maxD */}
|
||||||
|
<TextField
|
||||||
|
variant="outlined"
|
||||||
|
type="number"
|
||||||
|
value={maxDiameter}
|
||||||
|
onChange={e => {
|
||||||
|
if (!isNaN(+e.target.value)) {
|
||||||
|
setMaxDiameter(+e.target.value);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
label={
|
||||||
|
"Maximum Diameter " +
|
||||||
|
(getDistanceUnit() === pond.DistanceUnit.DISTANCE_UNIT_FEET ? "(ft)" : "(m)")
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return <React.Fragment>{binSelection()}</React.Fragment>;
|
||||||
|
}
|
||||||
2366
src/bin/BinSettings.tsx
Normal file
2366
src/bin/BinSettings.tsx
Normal file
File diff suppressed because it is too large
Load diff
67
src/common/DataImports/BinCables/BinCableImporter.tsx
Normal file
67
src/common/DataImports/BinCables/BinCableImporter.tsx
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
import bins from "common/DataImports/BinCables/bin_cable_data_with_count.json";
|
||||||
|
import brackets from "common/DataImports/BinCables/bracketData.json";
|
||||||
|
|
||||||
|
//note all length measurements are in feet
|
||||||
|
export interface Cable {
|
||||||
|
Orbit: number; //the orbit the cable is on in the bin
|
||||||
|
Length: number; //length of the cable
|
||||||
|
Nodes: number; //number of nodes on the cable
|
||||||
|
Type: number; //cable being temp or moisture
|
||||||
|
Count: number; //the number of similar cables
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CableSum {
|
||||||
|
Orbit: number;
|
||||||
|
NumberOfCables: number;
|
||||||
|
DistanceFromCenter: number;
|
||||||
|
Bracket: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface jsonBin {
|
||||||
|
ID: number;
|
||||||
|
Manufacturer: string;
|
||||||
|
Type: string;
|
||||||
|
Bottom: string;
|
||||||
|
Diameter: number;
|
||||||
|
Name: string;
|
||||||
|
Model: string;
|
||||||
|
Capacity: number;
|
||||||
|
Rings: number;
|
||||||
|
Sidewall: number;
|
||||||
|
Peak: number;
|
||||||
|
EaveToPeak: number;
|
||||||
|
RoofAngle: number;
|
||||||
|
HopperAngle: number;
|
||||||
|
CableSums: CableSum[];
|
||||||
|
Cables: Cable[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CableBracket {
|
||||||
|
ID: number;
|
||||||
|
Name: string;
|
||||||
|
DealerPrice: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ImportBins() {
|
||||||
|
return bins;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ImportBinMap() {
|
||||||
|
return new Map(bins.map(bin => [bin.ID, bin]));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBinModel(id: number) {
|
||||||
|
return ImportBinMap().get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BracketMap() {
|
||||||
|
let bMap = new Map<number, CableBracket>();
|
||||||
|
brackets.forEach(b => {
|
||||||
|
bMap.set(b.ID, b);
|
||||||
|
});
|
||||||
|
return bMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBracket(bracketModel: number) {
|
||||||
|
return BracketMap().get(bracketModel);
|
||||||
|
}
|
||||||
131243
src/common/DataImports/BinCables/bin_cable_data_with_count.json
Normal file
131243
src/common/DataImports/BinCables/bin_cable_data_with_count.json
Normal file
File diff suppressed because it is too large
Load diff
17
src/common/DataImports/BinCables/bracketData.json
Normal file
17
src/common/DataImports/BinCables/bracketData.json
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"ID": 1,
|
||||||
|
"Name": "CABL-BRK1",
|
||||||
|
"DealerPrice": 210
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ID": 2,
|
||||||
|
"Name": "CABL-BRK2",
|
||||||
|
"DealerPrice": 217.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ID": 3,
|
||||||
|
"Name": "CABL-BRK3",
|
||||||
|
"DealerPrice": 87
|
||||||
|
}
|
||||||
|
]
|
||||||
47
src/common/OutlinedBox.tsx
Normal file
47
src/common/OutlinedBox.tsx
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { Box, BoxProps, Theme, InputLabel } from "@mui/material";
|
||||||
|
import { makeStyles } from "@mui/styles";
|
||||||
|
// import NotchedOutline from "@material-ui/core/OutlinedInput/NotchedOutline";
|
||||||
|
import React, { PropsWithChildren } from "react";
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) => {
|
||||||
|
return ({
|
||||||
|
inputLabel: {
|
||||||
|
position: "absolute",
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
transform: "translate(0, 24px) scale(1)"
|
||||||
|
},
|
||||||
|
notchedOutline: {
|
||||||
|
borderRadius: theme.shape.borderRadius,
|
||||||
|
opacity: theme.palette.action.activatedOpacity
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
interface Props extends BoxProps {
|
||||||
|
label: string | React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const OutlinedBox = (props: PropsWithChildren<Props>) => {
|
||||||
|
const { label, children } = props;
|
||||||
|
const classes = useStyles();
|
||||||
|
const [labelWidth, setLabelWidth] = React.useState(0);
|
||||||
|
const labelRef = React.useRef<HTMLLabelElement>(null);
|
||||||
|
React.useEffect(() => {
|
||||||
|
const labelNode = labelRef.current;
|
||||||
|
setLabelWidth(labelNode != null ? labelNode.offsetWidth : 0);
|
||||||
|
}, [label]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box position="relative" width="100%" {...props}>
|
||||||
|
<InputLabel ref={labelRef} variant="outlined" className={classes.inputLabel} shrink>
|
||||||
|
{label}
|
||||||
|
</InputLabel>
|
||||||
|
{/* TODO: Add the notched outline part??? it's no longer in mui */}
|
||||||
|
{/* <NotchedOutline notched labelWidth={labelWidth} classes={{ root: classes.notchedOutline }} /> */}
|
||||||
|
<Box position="relative">{children}</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default OutlinedBox;
|
||||||
47
src/common/TrigFunctions.tsx
Normal file
47
src/common/TrigFunctions.tsx
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
/**
|
||||||
|
* Converts degrees to radians
|
||||||
|
* @param angle angle in degrees
|
||||||
|
* @returns angle in radians
|
||||||
|
*/
|
||||||
|
export function DegreesToRadians(angle: number) {
|
||||||
|
return angle * (Math.PI / 180);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts radians to degrees
|
||||||
|
* @param angle angle in radians
|
||||||
|
* @returns angle in degrees
|
||||||
|
*/
|
||||||
|
export function RadiansToDegrees(angle: number) {
|
||||||
|
return angle * (180 / Math.PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes in the one side of a right triangle its adjacent angle to determine the length of the opposite line
|
||||||
|
* @param length the length of the known side
|
||||||
|
* @param angle in degrees of the angle adjacent to the know side
|
||||||
|
* @returns the length of the line opposite the angle
|
||||||
|
*/
|
||||||
|
export function TriangleOppositeLength(length: number, angle: number) {
|
||||||
|
return length * Math.tan(DegreesToRadians(angle));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* calculates the cubic volume of a cone, NOTE: radius and height must have the same units
|
||||||
|
* @param radius the radius of the base of the cone
|
||||||
|
* @param height the height at the peak of the cone
|
||||||
|
* @returns the cubic volume
|
||||||
|
*/
|
||||||
|
export function ConeVolume(radius: number, height: number) {
|
||||||
|
return Math.PI * Math.pow(radius, 2) * (height / 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* calculates the cubic volume of a cylinder, NOTE: radius and height must have the same units
|
||||||
|
* @param radius the radius of the cylinder
|
||||||
|
* @param height the height of the cylinder
|
||||||
|
* @returns the cubic volume
|
||||||
|
*/
|
||||||
|
export function CylinderVolume(radius: number, height: number) {
|
||||||
|
return Math.PI * Math.pow(radius, 2) * height;
|
||||||
|
}
|
||||||
|
|
@ -76,6 +76,8 @@ import { describeMeasurement } from "pbHelpers/MeasurementDescriber";
|
||||||
import { green, yellow } from "@mui/material/colors";
|
import { green, yellow } from "@mui/material/colors";
|
||||||
import { makeStyles } from "@mui/styles";
|
import { makeStyles } from "@mui/styles";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import BinSettings from "bin/BinSettings";
|
||||||
|
import AddBinFab from "bin/AddBinFab";
|
||||||
// import { useHistory } from "react-router";
|
// import { useHistory } from "react-router";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) => {
|
const useStyles = makeStyles((theme: Theme) => {
|
||||||
|
|
@ -736,12 +738,12 @@ export default function Bins(props: Props) {
|
||||||
setAddBinOpen(false);
|
setAddBinOpen(false);
|
||||||
}}
|
}}
|
||||||
disableAutoFocusItem>
|
disableAutoFocusItem>
|
||||||
{/* <MenuItem onClick={() => setAddBinOpen(true)} aria-label="Create Bin" button dense>
|
<MenuItem onClick={() => setAddBinOpen(true)} aria-label="Create Bin" dense>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<LibraryAdd className={classes.green} />
|
<LibraryAdd className={classes.green} />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText secondary="Create Bin" />
|
<ListItemText secondary="Create Bin" />
|
||||||
</MenuItem> */}
|
</MenuItem>
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
@ -1362,7 +1364,7 @@ export default function Bins(props: Props) {
|
||||||
</Grid>
|
</Grid>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
{/* <BinSettings
|
<BinSettings
|
||||||
mode="add"
|
mode="add"
|
||||||
canEdit={true}
|
canEdit={true}
|
||||||
open={addBinOpen}
|
open={addBinOpen}
|
||||||
|
|
@ -1376,7 +1378,7 @@ export default function Bins(props: Props) {
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<GrainBagSettings
|
{/* <GrainBagSettings
|
||||||
open={addBagOpen}
|
open={addBagOpen}
|
||||||
close={() => {
|
close={() => {
|
||||||
setAddBagOpen(false);
|
setAddBagOpen(false);
|
||||||
|
|
@ -1391,7 +1393,7 @@ export default function Bins(props: Props) {
|
||||||
requiredUrlAffix={"bins"}
|
requiredUrlAffix={"bins"}
|
||||||
/> */}
|
/> */}
|
||||||
{binMenu()}
|
{binMenu()}
|
||||||
{/* <AddBinFab onClick={() => setAddBinOpen(true)} pulse={binsTotal < 1 && !allLoading} /> */}
|
<AddBinFab onClick={() => setAddBinOpen(true)} pulse={binsTotal < 1 && !allLoading} />
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
203
src/pbHelpers/Bin.ts
Normal file
203
src/pbHelpers/Bin.ts
Normal file
|
|
@ -0,0 +1,203 @@
|
||||||
|
import { pond, quack } from "protobuf-ts/pond";
|
||||||
|
import { stringToComponentId } from "./Component";
|
||||||
|
import { capitalize } from "utils/strings";
|
||||||
|
import { or } from "utils";
|
||||||
|
|
||||||
|
interface BinShapeDescriber {
|
||||||
|
key: pond.BinShape;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GetBinShapeDescribers = (): BinShapeDescriber[] => {
|
||||||
|
return [
|
||||||
|
{ key: pond.BinShape.BIN_SHAPE_UNKNOWN, label: "Unknown" },
|
||||||
|
{ key: pond.BinShape.BIN_SHAPE_FLAT_BOTTOM, label: "Flat Bottom" },
|
||||||
|
{ key: pond.BinShape.BIN_SHAPE_HOPPER_BOTTOM, label: "Hopper" }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const HasGrainCable = (bin?: pond.BinSettings): boolean => {
|
||||||
|
if (bin && bin.deviceComponents) {
|
||||||
|
bin.deviceComponents.forEach(dc => {
|
||||||
|
dc.components.forEach(c => {
|
||||||
|
if (stringToComponentId(c).type === quack.ComponentType.COMPONENT_TYPE_GRAIN_CABLE)
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const HasPlenumSensor = (bin?: pond.BinSettings): boolean => {
|
||||||
|
if (bin && bin.deviceComponents) {
|
||||||
|
bin.deviceComponents.forEach(dc => {
|
||||||
|
dc.components.forEach(c => {
|
||||||
|
let component = stringToComponentId(c);
|
||||||
|
if (component.type === quack.ComponentType.COMPONENT_TYPE_DHT) return true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const keyTranslator = new Map<keyof pond.BinSettings, string>([
|
||||||
|
["name", "Name"],
|
||||||
|
["specs", "Specifications"],
|
||||||
|
["mode", "Mode of Operation"],
|
||||||
|
["fan", "Fan Type"],
|
||||||
|
["location", "Location"],
|
||||||
|
["inventory", "Inventory"],
|
||||||
|
["deviceComponents", "Device Components"]
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Keys will be stringified by default if not found in the keyTranslator
|
||||||
|
export function TranslateKey(key: keyof pond.BinSettings): string {
|
||||||
|
let translatedKey = keyTranslator.get(key);
|
||||||
|
return translatedKey ? translatedKey : capitalize(key.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
const valueTranslator = new Map<keyof pond.BinSettings, (device: pond.BinSettings) => string>([
|
||||||
|
["name", device => device.name],
|
||||||
|
[
|
||||||
|
"specs",
|
||||||
|
device => {
|
||||||
|
let s: string = "";
|
||||||
|
if (device.specs) {
|
||||||
|
s = s + "Capacity: " + device.specs.bushelCapacity + " (bu)";
|
||||||
|
switch (device.specs.shape) {
|
||||||
|
case pond.BinShape.BIN_SHAPE_FLAT_BOTTOM:
|
||||||
|
s = s + "\nShape: Flat Bottom";
|
||||||
|
break;
|
||||||
|
case pond.BinShape.BIN_SHAPE_HOPPER_BOTTOM:
|
||||||
|
s = s + "\nShape: Hopper Bottom";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
s = s + "Unknown";
|
||||||
|
}
|
||||||
|
s = s + "\nHeight: " + device.specs.heightCm + " (cm)";
|
||||||
|
s = s + "\nDiameter: " + device.specs.diameterCm + " (cm)";
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"mode",
|
||||||
|
device => {
|
||||||
|
if (device.mode === undefined) {
|
||||||
|
return "None";
|
||||||
|
}
|
||||||
|
if (device.mode === null) {
|
||||||
|
return "None";
|
||||||
|
}
|
||||||
|
switch (device.mode) {
|
||||||
|
case pond.BinMode.BIN_MODE_COOLDOWN:
|
||||||
|
return "Cool Down";
|
||||||
|
case pond.BinMode.BIN_MODE_DRYING:
|
||||||
|
return "Drying";
|
||||||
|
case pond.BinMode.BIN_MODE_NONE:
|
||||||
|
return "None";
|
||||||
|
case pond.BinMode.BIN_MODE_STORAGE:
|
||||||
|
return "Storage";
|
||||||
|
case pond.BinMode.BIN_MODE_HYDRATING:
|
||||||
|
return "Hydrating";
|
||||||
|
default:
|
||||||
|
return "None";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"fan",
|
||||||
|
device => {
|
||||||
|
switch (device.fan?.type) {
|
||||||
|
case pond.FanType.FAN_TYPE_CENTRIFUGAL_HIGH_SPEED:
|
||||||
|
return "Centrifugal High Speed";
|
||||||
|
case pond.FanType.FAN_TYPE_CENTRIFUGAL_INLINE:
|
||||||
|
return "Centrifugal Inline";
|
||||||
|
case pond.FanType.FAN_TYPE_CENTRIFUGAL_LOW_SPEED:
|
||||||
|
return "Centrifugal Low Speed";
|
||||||
|
case pond.FanType.FAN_TYPE_UNKNOWN:
|
||||||
|
return "Unknown";
|
||||||
|
default:
|
||||||
|
return "None";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"location",
|
||||||
|
device => {
|
||||||
|
let long = device.location?.longitude;
|
||||||
|
let lat = device.location?.latitude;
|
||||||
|
if (long && lat) {
|
||||||
|
return "Long: " + Math.round(long * 100) / 100 + ", Lat: " + Math.round(lat * 100) / 100;
|
||||||
|
}
|
||||||
|
return "No coords to compare";
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"inventory",
|
||||||
|
device => {
|
||||||
|
let s: string = "";
|
||||||
|
if (device.inventory) {
|
||||||
|
s = s + "Grain Type: ";
|
||||||
|
switch (device.inventory.grainType) {
|
||||||
|
case pond.Grain.GRAIN_BARLEY:
|
||||||
|
s = s + "Barley,\n";
|
||||||
|
break;
|
||||||
|
case pond.Grain.GRAIN_BUCKWHEAT:
|
||||||
|
s = s + "Buckwheat,\n";
|
||||||
|
break;
|
||||||
|
case pond.Grain.GRAIN_CANOLA:
|
||||||
|
s = s + "Canola,\n";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
s = s + "Grain,\n";
|
||||||
|
}
|
||||||
|
s = s + "Use: ";
|
||||||
|
switch (device.inventory.grainUse) {
|
||||||
|
case pond.GrainUse.GRAIN_USE_COMMERCIAL:
|
||||||
|
s = s + "Commercial,\n";
|
||||||
|
break;
|
||||||
|
case pond.GrainUse.GRAIN_USE_FEED:
|
||||||
|
s = s + "Feed,\n";
|
||||||
|
break;
|
||||||
|
case pond.GrainUse.GRAIN_USE_SEED:
|
||||||
|
s = s + "Seed,\n";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
s = s + "Unknown,\n";
|
||||||
|
}
|
||||||
|
s = s + "Amount: " + device.inventory.grainBushels + " (bu)";
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"deviceComponents",
|
||||||
|
device => {
|
||||||
|
let s: string = "";
|
||||||
|
device.deviceComponents.forEach(dev => {
|
||||||
|
dev.components.forEach(comp => {
|
||||||
|
s = s + comp + "\n";
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"theme",
|
||||||
|
device => {
|
||||||
|
return "Bin Theme";
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Values will be stringified by default if its key is not found in the valueTranslator
|
||||||
|
export function TranslateValue(key: keyof pond.BinSettings, device: pond.BinSettings) {
|
||||||
|
let translatorFunc = valueTranslator.get(key);
|
||||||
|
let value: any = or(device[key], "");
|
||||||
|
let d: string;
|
||||||
|
d = or(value.toString(), "");
|
||||||
|
return translatorFunc ? translatorFunc(device) : device[key] ? capitalize(d) : "";
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,7 @@ export { HTTPContext, useHTTP } from "./http";
|
||||||
export {
|
export {
|
||||||
useBackpackAPI,
|
useBackpackAPI,
|
||||||
useBinAPI,
|
useBinAPI,
|
||||||
// useBinYardAPI,
|
useBinYardAPI,
|
||||||
useNoteAPI,
|
useNoteAPI,
|
||||||
useNotificationAPI,
|
useNotificationAPI,
|
||||||
useComponentAPI,
|
useComponentAPI,
|
||||||
|
|
|
||||||
113
src/providers/pond/binYardAPI.tsx
Normal file
113
src/providers/pond/binYardAPI.tsx
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
import { useHTTP } from "hooks";
|
||||||
|
import { createContext, PropsWithChildren, useContext } from "react";
|
||||||
|
import { pond } from "protobuf-ts/pond";
|
||||||
|
import { or } from "utils/types";
|
||||||
|
import { pondURL } from "./pond";
|
||||||
|
import { AxiosResponse } from "axios";
|
||||||
|
import { useGlobalState } from "providers";
|
||||||
|
|
||||||
|
export interface IBinYardAPIContext {
|
||||||
|
addBinYard: (bin: pond.BinYardSettings) => Promise<AxiosResponse<pond.AddBinYardResponse>>;
|
||||||
|
updateBinYard: (
|
||||||
|
key: string,
|
||||||
|
binYard: pond.BinYardSettings,
|
||||||
|
asRoot?: true
|
||||||
|
) => Promise<AxiosResponse<pond.UpdateBinYardResponse>>;
|
||||||
|
removeBinYard: (key: string) => Promise<AxiosResponse<pond.RemoveBinYardResponse>>;
|
||||||
|
getBinYard: (key: string) => Promise<AxiosResponse<pond.BinYard>>;
|
||||||
|
listBinYards: (
|
||||||
|
limit: number,
|
||||||
|
offset: number,
|
||||||
|
order?: "asc" | "desc",
|
||||||
|
orderBy?: string,
|
||||||
|
search?: string,
|
||||||
|
asRoot?: boolean,
|
||||||
|
specificUser?: string
|
||||||
|
) => Promise<AxiosResponse<pond.ListBinYardsResponse>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BinYardAPIContext = createContext<IBinYardAPIContext>({} as IBinYardAPIContext);
|
||||||
|
|
||||||
|
interface Props {}
|
||||||
|
|
||||||
|
export default function BinYardProvider(props: PropsWithChildren<Props>) {
|
||||||
|
const { children } = props;
|
||||||
|
const { get, post, put, del } = useHTTP();
|
||||||
|
const [{ as }] = useGlobalState();
|
||||||
|
|
||||||
|
const addBinYard = (binYard: pond.BinYardSettings) => {
|
||||||
|
if (as) return post<pond.AddBinYardResponse>(pondURL(`/binyard?as=${as}`), binYard);
|
||||||
|
return post<pond.AddBinYardResponse>(pondURL("/binyard"), binYard);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateBinYard = (key: string, binYard: pond.BinYardSettings, asRoot?: boolean) => {
|
||||||
|
if (as) {
|
||||||
|
if (asRoot) {
|
||||||
|
return put<pond.UpdateBinYardResponse>(
|
||||||
|
pondURL(
|
||||||
|
"/binyards/" + key + (asRoot ? "?asRoot=" + asRoot.toString() : "") + "&as=" + as
|
||||||
|
),
|
||||||
|
binYard
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return put<pond.UpdateBinYardResponse>(pondURL("/binyards/" + key + "?as=" + as), binYard);
|
||||||
|
}
|
||||||
|
return put<pond.UpdateBinYardResponse>(
|
||||||
|
pondURL("/binyards/" + key + (asRoot ? "?asRoot=" + asRoot.toString() : "")),
|
||||||
|
binYard
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeBinYard = (key: string) => {
|
||||||
|
if (as) return del<pond.RemoveBinYardResponse>(pondURL("/binyard/" + key + "?as=" + as));
|
||||||
|
return del<pond.RemoveBinYardResponse>(pondURL("/binyard/" + key));
|
||||||
|
};
|
||||||
|
|
||||||
|
const getBinYard = (key: string) => {
|
||||||
|
if (as) return get<pond.BinYard>(pondURL("/binYard/" + key + "?as=" + as));
|
||||||
|
return get<pond.BinYard>(pondURL("/binYard/" + key));
|
||||||
|
};
|
||||||
|
|
||||||
|
const listBinYards = (
|
||||||
|
limit: number,
|
||||||
|
offset: number,
|
||||||
|
order?: "asc" | "desc",
|
||||||
|
orderBy?: string,
|
||||||
|
search?: string,
|
||||||
|
asRoot?: boolean,
|
||||||
|
specificUser?: string
|
||||||
|
) => {
|
||||||
|
let asText = "";
|
||||||
|
if (as) asText = "&as=" + as;
|
||||||
|
if (specificUser) asText = "&as=" + specificUser;
|
||||||
|
return get<pond.ListBinYardsResponse>(
|
||||||
|
pondURL(
|
||||||
|
"/binYards" +
|
||||||
|
"?limit=" +
|
||||||
|
limit +
|
||||||
|
"&offset=" +
|
||||||
|
offset +
|
||||||
|
("&order=" + or(order, "asc")) +
|
||||||
|
("&by=" + or(orderBy, "key")) +
|
||||||
|
(search ? "&search=" + search : "") +
|
||||||
|
(asRoot ? "&asRoot=" + asRoot.toString() : "") +
|
||||||
|
asText
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BinYardAPIContext.Provider
|
||||||
|
value={{
|
||||||
|
addBinYard,
|
||||||
|
removeBinYard,
|
||||||
|
getBinYard,
|
||||||
|
listBinYards,
|
||||||
|
updateBinYard
|
||||||
|
}}>
|
||||||
|
{children}
|
||||||
|
</BinYardAPIContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useBinYardAPI = () => useContext(BinYardAPIContext);
|
||||||
|
|
@ -15,6 +15,7 @@ import TagProvider, { useTagAPI } from "./tagAPI";
|
||||||
import FirmwareProvider, { useFirmwareAPI } from "./firmwareAPI";
|
import FirmwareProvider, { useFirmwareAPI } from "./firmwareAPI";
|
||||||
import ComponentProvider, { useComponentAPI } from "./componentAPI";
|
import ComponentProvider, { useComponentAPI } from "./componentAPI";
|
||||||
import InteractionProvider, { useInteractionsAPI } from "./interactionsAPI";
|
import InteractionProvider, { useInteractionsAPI } from "./interactionsAPI";
|
||||||
|
import BinYardProvider, { useBinYardAPI } from "./binYardAPI";
|
||||||
// import NoteProvider from "providers/noteAPI";
|
// import NoteProvider from "providers/noteAPI";
|
||||||
|
|
||||||
export const pondURL = (partial: string, demo: boolean = false): string => {
|
export const pondURL = (partial: string, demo: boolean = false): string => {
|
||||||
|
|
@ -46,7 +47,9 @@ export default function PondProvider(props: PropsWithChildren<any>) {
|
||||||
<FirmwareProvider>
|
<FirmwareProvider>
|
||||||
<ComponentProvider>
|
<ComponentProvider>
|
||||||
<InteractionProvider>
|
<InteractionProvider>
|
||||||
{children}
|
<BinYardProvider>
|
||||||
|
{children}
|
||||||
|
</BinYardProvider>
|
||||||
</InteractionProvider>
|
</InteractionProvider>
|
||||||
</ComponentProvider>
|
</ComponentProvider>
|
||||||
</FirmwareProvider>
|
</FirmwareProvider>
|
||||||
|
|
@ -76,6 +79,7 @@ export {
|
||||||
useInteractionsAPI,
|
useInteractionsAPI,
|
||||||
usePermissionAPI,
|
usePermissionAPI,
|
||||||
useBinAPI,
|
useBinAPI,
|
||||||
|
useBinYardAPI,
|
||||||
useGateAPI,
|
useGateAPI,
|
||||||
useGroupAPI,
|
useGroupAPI,
|
||||||
useNoteAPI,
|
useNoteAPI,
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
/* Bundler mode */
|
/* Bundler mode */
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
//"allowImportingTsExtensions": true,
|
//"allowImportingTsExtensions": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
//"moduleDetection": "force",
|
//"moduleDetection": "force",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"baseUrl": "src",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"esModuleInterop": true
|
"esModuleInterop": true
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue