468 lines
16 KiB
TypeScript
468 lines
16 KiB
TypeScript
import {
|
|
Button,
|
|
DialogActions,
|
|
DialogContent,
|
|
DialogTitle,
|
|
Grid2 as Grid,
|
|
InputAdornment,
|
|
Switch,
|
|
TextField,
|
|
Theme,
|
|
useTheme
|
|
} from "@mui/material";
|
|
import { makeStyles } from "@mui/styles";
|
|
import ResponsiveDialog from "common/ResponsiveDialog";
|
|
import SearchSelect, { Option } from "common/SearchSelect";
|
|
import GrainDescriber, { GrainOptions, ToGrainOption } from "grain/GrainDescriber";
|
|
import GrainTransaction from "grain/GrainTransaction";
|
|
import { clone } from "lodash";
|
|
import { GrainBag } from "models/GrainBag";
|
|
import moment from "moment";
|
|
import { pond } from "protobuf-ts/pond";
|
|
import { useGlobalState, useGrainBagAPI, useSnackbar } from "providers";
|
|
import React, { useEffect, useState } from "react";
|
|
import { useNavigate } from "react-router-dom";
|
|
|
|
const useStyles = makeStyles((theme: Theme) => {
|
|
return ({
|
|
bottomSpacing: {
|
|
marginBottom: theme.spacing(1)
|
|
}
|
|
})
|
|
});
|
|
|
|
interface Props {
|
|
open: boolean;
|
|
close: (updatedBag?: GrainBag) => void;
|
|
coordinates?: { start: pond.Coordinates; end: pond.Coordinates };
|
|
grainBag?: GrainBag;
|
|
}
|
|
|
|
export default function GrainBagSettings(props: Props) {
|
|
const { open, close, grainBag, coordinates } = props;
|
|
const classes = useStyles();
|
|
const grainBagAPI = useGrainBagAPI();
|
|
const [bagName, setBagName] = useState("");
|
|
const [bagDiameterM, setBagDiameterM] = useState(0);
|
|
const [bagLengthM, setBagLengthM] = useState(0);
|
|
const [bagDiameterDisplay, setBagDiameterDisplay] = useState(0);
|
|
const [bagLengthDispla, setBagLengthDisplay] = useState(0);
|
|
const [{ user, as }] = useGlobalState();
|
|
const [isCustom, setIsCustom] = useState<boolean>(false);
|
|
const [customType, setCustomType] = useState("");
|
|
const [supportedGrainType, setSupportedGrainType] = useState(pond.Grain.GRAIN_INVALID);
|
|
const [selectedGrainOp, setSelectedGrainOp] = useState<Option | null>(null);
|
|
const grainOptions = GrainOptions();
|
|
const [grainSubtype, setGrainSubtype] = useState("");
|
|
const [grainCapacity, setGrainCapacity] = useState(0);
|
|
const [grainFill, setGrainFill] = useState("");
|
|
const [grainBushels, setGrainBushels] = useState(0);
|
|
const [fillDate, setFillDate] = useState(moment().format("YYYY-MM-DD"));
|
|
const [initialMoisture, setInitialMoisture] = useState(0);
|
|
// const history = useHistory();
|
|
const navigate = useNavigate()
|
|
const { openSnack } = useSnackbar();
|
|
const theme = useTheme();
|
|
const [grainUpdate, setGrainUpdate] = useState(false);
|
|
const [grainDiff, setGrainDiff] = useState(0);
|
|
const [bushelConversion, setBushelConversion] = useState(0);
|
|
|
|
useEffect(() => {
|
|
if (grainBag) {
|
|
//if a grainbag is passed in set all the state values
|
|
setBagDiameterM(grainBag.settings.diameter);
|
|
setBagDiameterDisplay(
|
|
user.settings.distanceUnit === pond.DistanceUnit.DISTANCE_UNIT_FEET
|
|
? grainBag.settings.diameter * 3.281
|
|
: grainBag.settings.diameter
|
|
);
|
|
setBagLengthM(grainBag.settings.length);
|
|
setBagLengthDisplay(
|
|
user.settings.distanceUnit === pond.DistanceUnit.DISTANCE_UNIT_FEET
|
|
? grainBag.settings.length * 3.281
|
|
: grainBag.settings.length
|
|
);
|
|
let grainVal = grainBag.bushels();
|
|
if (user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE) {
|
|
grainVal = grainBag.bushels() / grainBag.bushelsPerTonne();
|
|
}else if (user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TON) {
|
|
grainVal = grainBag.bushels() / (grainBag.bushelsPerTonne() * 0.907)
|
|
}
|
|
setGrainFill(grainVal.toFixed(2));
|
|
setBagName(grainBag.name());
|
|
setIsCustom(grainBag.isCustom());
|
|
setSupportedGrainType(grainBag.settings.supportedGrain);
|
|
setSelectedGrainOp(ToGrainOption(grainBag.settings.supportedGrain));
|
|
setCustomType(grainBag.settings.customGrain);
|
|
setGrainSubtype(grainBag.settings.grainSubtype);
|
|
setGrainCapacity(grainBag.capacity());
|
|
setGrainBushels(grainBag.bushels());
|
|
setFillDate(grainBag.settings.fillDate);
|
|
setInitialMoisture(grainBag.settings.initialMoisture);
|
|
}
|
|
}, [grainBag, user]);
|
|
|
|
const removeGrainBag = () => {
|
|
if (!grainBag) return;
|
|
grainBagAPI
|
|
.removeGrainBag(grainBag.key(), as)
|
|
.then(resp => {
|
|
openSnack("Removed Grain Bag");
|
|
close();
|
|
navigate("/bins");
|
|
})
|
|
.catch(err => {
|
|
openSnack("Failed to remove Grain Bag");
|
|
});
|
|
};
|
|
|
|
const submit = () => {
|
|
//if a bag was passed in do an update
|
|
let tonneConversion = bushelConversion
|
|
if(user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TON){
|
|
tonneConversion = bushelConversion / 0.907
|
|
}
|
|
if (grainBag) {
|
|
grainBag.title = bagName;
|
|
grainBag.settings.initialMoisture = initialMoisture;
|
|
grainBag.settings.diameter = bagDiameterM;
|
|
grainBag.settings.length = bagLengthM;
|
|
grainBag.settings.customGrain = customType;
|
|
grainBag.settings.supportedGrain = supportedGrainType;
|
|
grainBag.settings.bushelCapacity = grainCapacity;
|
|
grainBag.settings.grainSubtype = grainSubtype;
|
|
grainBag.settings.fillDate = fillDate;
|
|
grainBag.settings.bushelsPerTonne = tonneConversion;
|
|
grainBag.settings.storageType = isCustom
|
|
? pond.BinStorage.BIN_STORAGE_UNSUPPORTED_GRAIN
|
|
: pond.BinStorage.BIN_STORAGE_SUPPORTED_GRAIN;
|
|
|
|
if (grainBag.bushels() !== grainBushels) {
|
|
//grainBag.settings.currentBushels = grainBushels;
|
|
setGrainDiff(grainBushels - grainBag.bushels());
|
|
setGrainUpdate(true);
|
|
} else {
|
|
grainBagAPI
|
|
.updateGrainBag(grainBag.key(), bagName, grainBag.settings, as)
|
|
.then(resp => {
|
|
openSnack("Grain Bag Updated");
|
|
let updatedBag = clone(grainBag);
|
|
close(updatedBag);
|
|
})
|
|
.catch(err => {
|
|
openSnack("Failed to update Grain Bag");
|
|
close();
|
|
});
|
|
}
|
|
} else {
|
|
//otherwise make a new one
|
|
let settings = pond.GrainBagSettings.create();
|
|
settings.initialMoisture = initialMoisture;
|
|
settings.diameter = bagDiameterM;
|
|
settings.length = bagLengthM;
|
|
settings.customGrain = customType;
|
|
settings.supportedGrain = supportedGrainType;
|
|
settings.bushelCapacity = grainCapacity;
|
|
settings.currentBushels = grainBushels;
|
|
settings.grainSubtype = grainSubtype;
|
|
settings.fillDate = fillDate;
|
|
settings.bushelsPerTonne = tonneConversion;
|
|
settings.storageType = isCustom
|
|
? pond.BinStorage.BIN_STORAGE_UNSUPPORTED_GRAIN
|
|
: pond.BinStorage.BIN_STORAGE_SUPPORTED_GRAIN;
|
|
if (coordinates) {
|
|
settings.startLocation = coordinates.start;
|
|
settings.endLocation = coordinates.end;
|
|
}
|
|
|
|
grainBagAPI
|
|
.addGrainBag(bagName, settings, as)
|
|
.then(resp => {
|
|
openSnack("New grain bag added");
|
|
let newBag = GrainBag.create(
|
|
pond.GrainBag.fromObject({
|
|
title: bagName,
|
|
key: resp.data.key,
|
|
settings: settings
|
|
})
|
|
);
|
|
close(newBag);
|
|
})
|
|
.catch(() => {
|
|
openSnack("Failed to add new grain bag");
|
|
});
|
|
}
|
|
};
|
|
|
|
const grainBox = () => {
|
|
if (isCustom) {
|
|
return (
|
|
<React.Fragment>
|
|
<TextField
|
|
label="Grain"
|
|
value={customType}
|
|
type="text"
|
|
onChange={event => {
|
|
setCustomType(event.target.value);
|
|
}}
|
|
fullWidth
|
|
className={classes.bottomSpacing}
|
|
variant="outlined"
|
|
/>
|
|
<TextField
|
|
label="Bushels Per Tonne"
|
|
value={bushelConversion}
|
|
type="number"
|
|
onChange={event => {
|
|
setBushelConversion(+event.target.value);
|
|
}}
|
|
fullWidth
|
|
className={classes.bottomSpacing}
|
|
variant="outlined"
|
|
/>
|
|
</React.Fragment>
|
|
);
|
|
} else {
|
|
return (
|
|
<SearchSelect
|
|
style={{ marginBottom: theme.spacing(1) }}
|
|
label="Type"
|
|
selected={selectedGrainOp}
|
|
changeSelection={option => {
|
|
let newGrainType = option
|
|
? pond.Grain[option.value as keyof typeof pond.Grain]
|
|
: pond.Grain.GRAIN_INVALID;
|
|
setSupportedGrainType(newGrainType);
|
|
setBushelConversion(GrainDescriber(newGrainType).bushelsPerTonne);
|
|
setSelectedGrainOp(option);
|
|
}}
|
|
group
|
|
//disabled={!canEdit}
|
|
options={grainOptions}
|
|
/>
|
|
);
|
|
}
|
|
};
|
|
|
|
const formInvalid = () => {
|
|
let invalid = true;
|
|
if (bagName !== "" && grainCapacity > 0) {
|
|
invalid = false;
|
|
}
|
|
return invalid;
|
|
};
|
|
|
|
const grainUnitDisplay = () => {
|
|
if(bushelConversion > 1){
|
|
if(user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE){
|
|
return "mT"
|
|
}else if (user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TON){
|
|
return "t"
|
|
}
|
|
}else{
|
|
return "bu"
|
|
}
|
|
}
|
|
|
|
return (
|
|
<React.Fragment>
|
|
<GrainTransaction
|
|
open={grainUpdate}
|
|
mainObject={grainBag ?? GrainBag.create()}
|
|
grainAdjustment={grainDiff}
|
|
close={() => {
|
|
setGrainUpdate(false);
|
|
setGrainDiff(0);
|
|
//setPendingGrainAmount(undefined);
|
|
//resetFillPercentage();
|
|
//setGrainDiff(0);
|
|
}}
|
|
callback={() => {
|
|
let updatedBag = clone(grainBag);
|
|
close(updatedBag);
|
|
}}
|
|
/>
|
|
<ResponsiveDialog
|
|
open={open}
|
|
onClose={() => {
|
|
close();
|
|
}}>
|
|
<DialogTitle>Grain Bag Settings</DialogTitle>
|
|
<DialogContent>
|
|
<TextField
|
|
label="Grain Bag Name"
|
|
fullWidth
|
|
variant="outlined"
|
|
value={bagName}
|
|
onChange={e => setBagName(e.target.value)}
|
|
className={classes.bottomSpacing}
|
|
/>
|
|
<TextField
|
|
label="Bag Diameter"
|
|
InputProps={{
|
|
endAdornment: (
|
|
<InputAdornment position="end">
|
|
{user.settings.distanceUnit === pond.DistanceUnit.DISTANCE_UNIT_FEET ? "ft" : "m"}
|
|
</InputAdornment>
|
|
)
|
|
}}
|
|
fullWidth
|
|
variant="outlined"
|
|
type="number"
|
|
value={bagDiameterDisplay}
|
|
onChange={e => {
|
|
setBagDiameterDisplay(+e.target.value);
|
|
let inMeters = +e.target.value;
|
|
if (user.settings.distanceUnit === pond.DistanceUnit.DISTANCE_UNIT_FEET) {
|
|
//if the users preferred unit is in feet that means the value that will be saved needs to be converted to meters
|
|
inMeters = inMeters / 3.281;
|
|
}
|
|
setBagDiameterM(inMeters);
|
|
}}
|
|
className={classes.bottomSpacing}
|
|
/>
|
|
<TextField
|
|
label="Bag Length"
|
|
fullWidth
|
|
variant="outlined"
|
|
InputProps={{
|
|
endAdornment: (
|
|
<InputAdornment position="end">
|
|
{user.settings.distanceUnit === pond.DistanceUnit.DISTANCE_UNIT_FEET ? "ft" : "m"}
|
|
</InputAdornment>
|
|
)
|
|
}}
|
|
type="number"
|
|
value={bagLengthDispla}
|
|
onChange={e => {
|
|
setBagLengthDisplay(+e.target.value);
|
|
let inMeters = +e.target.value;
|
|
if (user.settings.distanceUnit === pond.DistanceUnit.DISTANCE_UNIT_FEET) {
|
|
//if the users preferred unit is in feet that means the value that will be saved needs to be converted to meters
|
|
inMeters = inMeters / 3.281;
|
|
}
|
|
setBagLengthM(inMeters);
|
|
}}
|
|
className={classes.bottomSpacing}
|
|
/>
|
|
<TextField
|
|
fullWidth
|
|
type="date"
|
|
label="Fill Date"
|
|
value={fillDate}
|
|
InputLabelProps={{ shrink: true }}
|
|
onChange={e => setFillDate(e.target.value)}
|
|
/>
|
|
<Grid container alignItems="center">
|
|
<Grid>Grain</Grid>
|
|
<Grid>
|
|
<Switch
|
|
color="default"
|
|
value={isCustom}
|
|
checked={isCustom}
|
|
onChange={(_, checked) => {
|
|
setIsCustom(checked);
|
|
if (checked) {
|
|
setSupportedGrainType(pond.Grain.GRAIN_CUSTOM);
|
|
} else {
|
|
setCustomType("");
|
|
}
|
|
}}
|
|
name="storage"
|
|
/>
|
|
</Grid>
|
|
<Grid>Custom</Grid>
|
|
</Grid>
|
|
{grainBox()}
|
|
<TextField
|
|
label="Grain Subtype"
|
|
fullWidth
|
|
variant="outlined"
|
|
value={grainSubtype}
|
|
onChange={e => setGrainSubtype(e.target.value)}
|
|
className={classes.bottomSpacing}
|
|
/>
|
|
<TextField
|
|
label="Initial Moisture"
|
|
fullWidth
|
|
type="number"
|
|
variant="outlined"
|
|
InputProps={{
|
|
endAdornment: <InputAdornment position="end">%</InputAdornment>
|
|
}}
|
|
value={initialMoisture}
|
|
onChange={e => setInitialMoisture(+e.target.value)}
|
|
className={classes.bottomSpacing}
|
|
/>
|
|
<TextField
|
|
label="Capacity"
|
|
fullWidth
|
|
type="number"
|
|
variant="outlined"
|
|
InputProps={{
|
|
endAdornment: <InputAdornment position="end">bu</InputAdornment>
|
|
}}
|
|
value={grainCapacity}
|
|
onChange={e => setGrainCapacity(+e.target.value)}
|
|
className={classes.bottomSpacing}
|
|
/>
|
|
<TextField
|
|
label={"Current Inventory"}
|
|
fullWidth
|
|
type="number"
|
|
variant="outlined"
|
|
InputProps={{
|
|
endAdornment: (
|
|
<InputAdornment position="end">
|
|
{grainUnitDisplay()}
|
|
</InputAdornment>
|
|
)
|
|
}}
|
|
value={grainFill}
|
|
onChange={e => {
|
|
let bushelVal = +e.target.value;
|
|
if (user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE || user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TON) {
|
|
//convert the number as a weight into the bushel value, no changing or conversions necessary
|
|
// since these are both user entered fields and should be the same unit (ton or tonne)
|
|
bushelVal = +e.target.value * (bushelConversion > 0 ? bushelConversion : 1);
|
|
}
|
|
if (bushelVal > grainCapacity) {
|
|
bushelVal = grainCapacity;
|
|
}
|
|
setGrainBushels(bushelVal);
|
|
setGrainFill(e.target.value);
|
|
}}
|
|
className={classes.bottomSpacing}
|
|
/>
|
|
</DialogContent>
|
|
<DialogActions>
|
|
<Grid container direction="row" justifyContent="space-between">
|
|
<Grid >
|
|
{grainBag && (
|
|
<Button
|
|
onClick={() => {
|
|
removeGrainBag();
|
|
}}
|
|
variant="contained"
|
|
style={{ background: "red" }}>
|
|
Delete
|
|
</Button>
|
|
)}
|
|
</Grid>
|
|
<Grid >
|
|
<Button
|
|
onClick={() => {
|
|
close();
|
|
}}>
|
|
Cancel
|
|
</Button>
|
|
<Button onClick={submit} disabled={formInvalid()} color="primary" variant="contained">
|
|
Confirm
|
|
</Button>
|
|
</Grid>
|
|
</Grid>
|
|
</DialogActions>
|
|
</ResponsiveDialog>
|
|
</React.Fragment>
|
|
);
|
|
}
|