added the harvest plan stuff for the map and field drawer
This commit is contained in:
parent
a8e7ae1041
commit
e2e061151b
11 changed files with 1610 additions and 36 deletions
108
src/harvestPlan/DuplicateHarvestPlan.tsx
Normal file
108
src/harvestPlan/DuplicateHarvestPlan.tsx
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
import {
|
||||
Button,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
MenuItem,
|
||||
TextField
|
||||
} from "@mui/material";
|
||||
import ResponsiveDialog from "common/ResponsiveDialog";
|
||||
import { Field, HarvestPlan, /*Task*/ } from "models";
|
||||
import moment from "moment";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
import { useHarvestPlanAPI/*, useTaskAPI*/ } from "providers";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
close: () => void;
|
||||
fields: Field[];
|
||||
plan: HarvestPlan;
|
||||
}
|
||||
|
||||
export default function DuplicateHarvestPlan(props: Props) {
|
||||
const { open, close, fields, plan } = props;
|
||||
const [newFieldIndex, setNewFieldIndex] = useState(0);
|
||||
//const [planTasks, setPlanTasks] = useState<Task[]>([]);
|
||||
const [title, setTitle] = useState("");
|
||||
const harvestPlanAPI = useHarvestPlanAPI();
|
||||
//const taskAPI = useTaskAPI();
|
||||
const [ready, setReady] = useState(false);
|
||||
|
||||
// const loadTasks = useCallback(() => {
|
||||
// taskAPI.listTasks(50, 0, "asc", "start", plan.key()).then(resp => {
|
||||
// setPlanTasks(resp.data.tasks.map(t => Task.any(t)));
|
||||
// setReady(true);
|
||||
// });
|
||||
// }, [taskAPI, plan]);
|
||||
|
||||
// useEffect(() => {
|
||||
// loadTasks();
|
||||
// }, [loadTasks]);
|
||||
|
||||
const duplicate = () => {
|
||||
let planSettings = HarvestPlan.clone(plan).settings;
|
||||
planSettings.field = fields[newFieldIndex].key();
|
||||
planSettings.createDate = moment.now();
|
||||
if (title !== "") {
|
||||
planSettings.title = title;
|
||||
}
|
||||
harvestPlanAPI.addHarvestPlan(planSettings).then(resp => {
|
||||
// let tasks = planTasks;
|
||||
// tasks.forEach(task => {
|
||||
// task.settings.objectKey = resp.data.harvestPlan;
|
||||
// taskAPI.addTask(task.settings);
|
||||
// });
|
||||
});
|
||||
close();
|
||||
};
|
||||
return (
|
||||
<ResponsiveDialog open={open} onClose={close}>
|
||||
<DialogTitle>Duplicate Crop Plan</DialogTitle>
|
||||
<DialogContent>
|
||||
<TextField
|
||||
margin="normal"
|
||||
fullWidth
|
||||
select
|
||||
label="Field"
|
||||
value={newFieldIndex}
|
||||
onChange={e => setNewFieldIndex(+e.target.value)}
|
||||
color="primary">
|
||||
{fields.map((option, i) => (
|
||||
<MenuItem key={option.key()} value={i}>
|
||||
{option.fieldName()}
|
||||
</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
<TextField
|
||||
fullWidth
|
||||
margin="normal"
|
||||
type="text"
|
||||
label="Title"
|
||||
value={title}
|
||||
helperText={"Leave blank to use the same title*"}
|
||||
onChange={e => setTitle(e.target.value)}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
onClick={close}
|
||||
variant="contained"
|
||||
style={{ color: "black", backgroundColor: "red" }}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={duplicate}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={
|
||||
!ready ||
|
||||
(fields[newFieldIndex] &&
|
||||
!fields[newFieldIndex].permissions.includes(pond.Permission.PERMISSION_WRITE))
|
||||
}>
|
||||
Duplicate
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</ResponsiveDialog>
|
||||
);
|
||||
}
|
||||
235
src/harvestPlan/HarvestPlanActions.tsx
Normal file
235
src/harvestPlan/HarvestPlanActions.tsx
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
import {
|
||||
Box,
|
||||
Button,
|
||||
IconButton,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
Menu,
|
||||
MenuItem,
|
||||
Theme
|
||||
} from "@mui/material";
|
||||
import MoreIcon from "@mui/icons-material/MoreVert";
|
||||
import ShareObjectIcon from "@mui/icons-material/Share";
|
||||
import ObjectUsersIcon from "@mui/icons-material/AccountCircle";
|
||||
import ObjectTeamsIcon from "@mui/icons-material/SupervisedUserCircle";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
import React, { useState } from "react";
|
||||
import ObjectUsers from "user/ObjectUsers";
|
||||
import ShareObject from "user/ShareObject";
|
||||
import { isOffline } from "utils/environment";
|
||||
import ObjectTeams from "teams/ObjectTeams";
|
||||
import { planScope, HarvestPlan, Field } from "models";
|
||||
import HarvestSettings from "./HarvestSettings";
|
||||
import DuplicateHarvestPlan from "./DuplicateHarvestPlan";
|
||||
import { Edit } from "@mui/icons-material";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { blue } from "@mui/material/colors";
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) => ({
|
||||
shareIcon: {
|
||||
color: blue["500"],
|
||||
"&:hover": {
|
||||
color: blue["600"]
|
||||
}
|
||||
},
|
||||
removeIcon: {
|
||||
color: "var(--status-alert)"
|
||||
},
|
||||
red: {
|
||||
color: "var(--status-alert)"
|
||||
}
|
||||
}));
|
||||
|
||||
interface Props {
|
||||
plan: HarvestPlan;
|
||||
planField: Field;
|
||||
fieldList: Field[];
|
||||
permissions: pond.Permission[];
|
||||
refreshCallback: (updatedPlan?: HarvestPlan) => void;
|
||||
hideNew?: boolean;
|
||||
}
|
||||
|
||||
interface OpenState {
|
||||
share: boolean;
|
||||
users: boolean;
|
||||
teams: boolean;
|
||||
settings: boolean;
|
||||
removeSelf: boolean;
|
||||
duplicate: boolean;
|
||||
}
|
||||
|
||||
export default function HarvestPlanActions(props: Props) {
|
||||
const classes = useStyles();
|
||||
const { plan, planField, fieldList, permissions, refreshCallback, hideNew } = props;
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
const [openState, setOpenState] = useState<OpenState>({
|
||||
share: false,
|
||||
users: false,
|
||||
teams: false,
|
||||
settings: false,
|
||||
removeSelf: false,
|
||||
duplicate: false
|
||||
});
|
||||
const [updatePlan, setUpdatePlan] = useState(false);
|
||||
|
||||
const groupMenu = () => {
|
||||
const canShare = permissions.includes(pond.Permission.PERMISSION_SHARE);
|
||||
const canManageUsers = permissions.includes(pond.Permission.PERMISSION_USERS);
|
||||
return (
|
||||
<Menu
|
||||
id="groupMenu"
|
||||
anchorEl={anchorEl}
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={() => setAnchorEl(null)}
|
||||
keepMounted
|
||||
disableAutoFocusItem>
|
||||
{!isOffline() &&
|
||||
planField.permissions.includes(pond.Permission.PERMISSION_WRITE) &&
|
||||
!hideNew && (
|
||||
<MenuItem
|
||||
dense
|
||||
onClick={() => {
|
||||
setUpdatePlan(false);
|
||||
setOpenState({ ...openState, settings: true });
|
||||
setAnchorEl(null);
|
||||
}}>
|
||||
<ListItemIcon></ListItemIcon>
|
||||
<ListItemText primary="New Plan" />
|
||||
</MenuItem>
|
||||
)}
|
||||
{!isOffline() && planField.permissions.includes(pond.Permission.PERMISSION_WRITE) && (
|
||||
<MenuItem
|
||||
dense
|
||||
onClick={() => {
|
||||
setOpenState({ ...openState, duplicate: true });
|
||||
setAnchorEl(null);
|
||||
}}>
|
||||
<ListItemIcon></ListItemIcon>
|
||||
<ListItemText primary="Duplicate Plan" />
|
||||
</MenuItem>
|
||||
)}
|
||||
{!isOffline() && canShare && (
|
||||
<MenuItem
|
||||
dense
|
||||
onClick={() => {
|
||||
setOpenState({ ...openState, share: true });
|
||||
setAnchorEl(null);
|
||||
}}>
|
||||
<ListItemIcon>
|
||||
<ShareObjectIcon className={classes.shareIcon} />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Share" />
|
||||
</MenuItem>
|
||||
)}
|
||||
{!isOffline() && canManageUsers && (
|
||||
<MenuItem
|
||||
dense
|
||||
onClick={() => {
|
||||
setOpenState({ ...openState, users: true });
|
||||
setAnchorEl(null);
|
||||
}}>
|
||||
<ListItemIcon>
|
||||
<ObjectUsersIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Users" />
|
||||
</MenuItem>
|
||||
)}
|
||||
{!isOffline() && canManageUsers && (
|
||||
<MenuItem
|
||||
dense
|
||||
onClick={() => {
|
||||
setOpenState({ ...openState, teams: true });
|
||||
setAnchorEl(null);
|
||||
}}>
|
||||
<ListItemIcon>
|
||||
<ObjectTeamsIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Teams" />
|
||||
</MenuItem>
|
||||
)}
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||
|
||||
const dialogs = () => {
|
||||
const key = plan.key();
|
||||
const label = plan.settings.title;
|
||||
return (
|
||||
<React.Fragment>
|
||||
<HarvestSettings
|
||||
field={planField}
|
||||
update={updatePlan}
|
||||
open={openState.settings}
|
||||
plan={plan.key() !== "" ? plan : undefined}
|
||||
close={(refresh, updatedPlan) => {
|
||||
setOpenState({ ...openState, settings: false });
|
||||
if (refresh) {
|
||||
refreshCallback(updatedPlan);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<DuplicateHarvestPlan
|
||||
open={openState.duplicate}
|
||||
close={() => {
|
||||
setOpenState({ ...openState, duplicate: false });
|
||||
refreshCallback();
|
||||
}}
|
||||
fields={fieldList}
|
||||
plan={plan}
|
||||
/>
|
||||
<ShareObject
|
||||
scope={planScope(key)}
|
||||
label={label}
|
||||
permissions={permissions}
|
||||
isDialogOpen={openState.share}
|
||||
closeDialogCallback={() => setOpenState({ ...openState, share: false })}
|
||||
/>
|
||||
<ObjectUsers
|
||||
scope={planScope(key)}
|
||||
label={label}
|
||||
permissions={permissions}
|
||||
isDialogOpen={openState.users}
|
||||
closeDialogCallback={() => setOpenState({ ...openState, users: false })}
|
||||
refreshCallback={refreshCallback}
|
||||
/>
|
||||
<ObjectTeams
|
||||
scope={planScope(key)}
|
||||
label="Teams"
|
||||
permissions={permissions}
|
||||
isDialogOpen={openState.teams}
|
||||
refreshCallback={refreshCallback}
|
||||
closeDialogCallback={() => setOpenState({ ...openState, teams: false })}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Box style={{ display: "flex", justifyContent: "space-between" }}>
|
||||
{plan.key() !== "" && (
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
setUpdatePlan(true);
|
||||
setOpenState({ ...openState, settings: true });
|
||||
}}>
|
||||
<Edit /> Edit or Add Activity
|
||||
</Button>
|
||||
)}
|
||||
<IconButton
|
||||
aria-owns={anchorEl ? "groupMenu" : undefined}
|
||||
aria-haspopup="true"
|
||||
onClick={(event: React.MouseEvent<HTMLButtonElement>) =>
|
||||
setAnchorEl(event.currentTarget)
|
||||
}>
|
||||
<MoreIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
|
||||
{groupMenu()}
|
||||
{dialogs()}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
209
src/harvestPlan/HarvestPlanDisplay.tsx
Normal file
209
src/harvestPlan/HarvestPlanDisplay.tsx
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
import {
|
||||
Box,
|
||||
Grid2 as Grid,
|
||||
Theme,
|
||||
Typography,
|
||||
IconButton,
|
||||
LinearProgress,
|
||||
Drawer
|
||||
} from "@mui/material";
|
||||
import { Notes } from "@mui/icons-material";
|
||||
import GrainDescriber from "grain/GrainDescriber";
|
||||
import { Field, HarvestPlan } from "models";
|
||||
import React, { useState } from "react";
|
||||
import { getThemeType } from "theme";
|
||||
import Chat from "chat/Chat";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
import { useMobile } from "hooks";
|
||||
import HarvestPlanActions from "./HarvestPlanActions";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
|
||||
interface Props {
|
||||
plan: HarvestPlan;
|
||||
permissions: pond.Permission[];
|
||||
planField: Field;
|
||||
loading: boolean;
|
||||
fieldList: Field[];
|
||||
changePlan: (updatedPlan?: HarvestPlan) => void;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) => ({
|
||||
dark: {
|
||||
backgroundColor: getThemeType() === "light" ? "rgb(245, 245, 245)" : "rgb(50, 50, 50)",
|
||||
padding: 5
|
||||
},
|
||||
light: {
|
||||
backgroundColor: getThemeType() === "light" ? "rgb(235, 235, 235)" : "rgb(60, 60, 60)",
|
||||
padding: 5
|
||||
},
|
||||
avatar: {
|
||||
color: getThemeType() === "light" ? theme.palette.common.black : theme.palette.common.white,
|
||||
backgroundColor: "transparent",
|
||||
width: theme.spacing(5),
|
||||
height: theme.spacing(5),
|
||||
border: "1px solid",
|
||||
borderColor: theme.palette.divider
|
||||
}
|
||||
}));
|
||||
|
||||
export default function HarvestPlanDisplay(props: Props) {
|
||||
const { plan, loading, planField, fieldList, changePlan, permissions } = props;
|
||||
const classes = useStyles();
|
||||
const [openNote, setOpenNote] = useState(false);
|
||||
const isMobile = useMobile();
|
||||
|
||||
const planTable = () => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Grid container direction="column">
|
||||
<Grid className={classes.dark}>
|
||||
<Box style={{ display: "flex", justifyContent: "space-between" }}>
|
||||
<Typography variant="subtitle1">Break Even Yield:</Typography>
|
||||
<Typography variant="subtitle1">
|
||||
{(
|
||||
(plan.totalEquipmentCost() + plan.totalMaterialCost()) /
|
||||
plan.bushelPrice()
|
||||
).toFixed(2)}{" "}
|
||||
BU
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid className={classes.light}>
|
||||
<Box style={{ display: "flex", justifyContent: "space-between" }}>
|
||||
<Typography variant="subtitle1">Break Even Sales Price</Typography>
|
||||
<Typography variant="subtitle1">
|
||||
$
|
||||
{(
|
||||
(plan.totalEquipmentCost() + plan.totalMaterialCost()) /
|
||||
plan.yieldTarget()
|
||||
).toFixed(2)}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid container direction="column" style={{ marginTop: 20 }}>
|
||||
<Grid className={classes.dark}>
|
||||
<Box style={{ display: "flex", justifyContent: "space-between" }}>
|
||||
<Typography variant="subtitle1">Harvest Year:</Typography>
|
||||
<Typography variant="subtitle1">{plan.harvestYear()}</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid className={classes.light}>
|
||||
<Box style={{ display: "flex", justifyContent: "space-between" }}>
|
||||
<Typography variant="subtitle1">Title:</Typography>
|
||||
<Typography variant="subtitle1">{plan.settings.title}</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid className={classes.dark}>
|
||||
<Box style={{ display: "flex", justifyContent: "space-between" }}>
|
||||
<Typography variant="subtitle1">Grain:</Typography>
|
||||
<Typography variant="subtitle1">{GrainDescriber(plan.cropType()).name}</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid className={classes.light}>
|
||||
<Box style={{ display: "flex", justifyContent: "space-between" }}>
|
||||
<Typography variant="subtitle1">Variant:</Typography>
|
||||
<Typography variant="subtitle1">{plan.grainType()}</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid className={classes.dark}>
|
||||
<Box style={{ display: "flex", justifyContent: "space-between" }}>
|
||||
<Typography variant="subtitle1">Bushel Price:</Typography>
|
||||
<Typography variant="subtitle1">${plan.bushelPrice()}</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid className={classes.light}>
|
||||
<Box style={{ display: "flex", justifyContent: "space-between" }}>
|
||||
<Typography variant="subtitle1">Yield Target:</Typography>
|
||||
<Typography variant="subtitle1">{plan.yieldTarget()}</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid className={classes.dark}>
|
||||
<Box style={{ display: "flex", justifyContent: "space-between" }}>
|
||||
<Typography variant="subtitle1">Total Cost (acre):</Typography>
|
||||
<Typography variant="subtitle1">
|
||||
${(plan.totalEquipmentCost() + plan.totalMaterialCost()).toFixed(2)}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid className={classes.light}>
|
||||
<Box style={{ display: "flex", justifyContent: "space-between" }}>
|
||||
<Typography variant="subtitle1">Total Cost:</Typography>
|
||||
<Typography variant="subtitle1">
|
||||
$
|
||||
{(
|
||||
(plan.totalEquipmentCost() + plan.totalMaterialCost()) *
|
||||
planField.acres()
|
||||
).toFixed(2)}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const noteDrawer = () => {
|
||||
return (
|
||||
<Drawer
|
||||
anchor={isMobile ? "bottom" : "right"}
|
||||
open={openNote}
|
||||
onClose={() => setOpenNote(false)}>
|
||||
<Box height={isMobile ? "50vh" : "100vh"} padding={2}>
|
||||
<Typography style={{ fontWeight: 650 }}>Notes</Typography>
|
||||
<Chat type={pond.NoteType.NOTE_TYPE_HARVEST_PLAN} objectKey={plan.key()} />
|
||||
</Box>
|
||||
</Drawer>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{loading ? (
|
||||
<Box paddingTop="20px">
|
||||
<LinearProgress color="primary" />
|
||||
</Box>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<IconButton
|
||||
disabled={plan.key() === ""}
|
||||
className={classes.avatar}
|
||||
onClick={() => setOpenNote(true)}>
|
||||
<Notes />
|
||||
</IconButton>
|
||||
<Box style={{ display: "flex", justifyContent: "space-between" }}>
|
||||
<Typography
|
||||
variant="h5"
|
||||
style={{ fontWeight: 700, marginTop: "auto", marginBottom: "auto" }}>
|
||||
Crop Plan{plan.key() !== "" ? " - " + plan.settings.title : " - None Found"}
|
||||
</Typography>
|
||||
{!isMobile && permissions.includes(pond.Permission.PERMISSION_WRITE) && (
|
||||
<HarvestPlanActions
|
||||
planField={planField}
|
||||
fieldList={fieldList}
|
||||
plan={plan}
|
||||
permissions={permissions}
|
||||
refreshCallback={updatedPlan => {
|
||||
changePlan(updatedPlan);
|
||||
}} //possibly need to re-load plan here
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
{isMobile && permissions.includes(pond.Permission.PERMISSION_WRITE) && (
|
||||
<HarvestPlanActions
|
||||
planField={planField}
|
||||
fieldList={fieldList}
|
||||
plan={plan}
|
||||
permissions={permissions}
|
||||
refreshCallback={updatedPlan => {
|
||||
changePlan(updatedPlan);
|
||||
}} //possibly need to re-load plan here
|
||||
/>
|
||||
)}
|
||||
{plan.key() !== "" && planTable()}
|
||||
</React.Fragment>
|
||||
)}
|
||||
{noteDrawer()}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
728
src/harvestPlan/HarvestSettings.tsx
Normal file
728
src/harvestPlan/HarvestSettings.tsx
Normal file
|
|
@ -0,0 +1,728 @@
|
|||
import {
|
||||
Box,
|
||||
Button,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
Grid2 as Grid,
|
||||
InputAdornment,
|
||||
MenuItem,
|
||||
Tab,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Tabs,
|
||||
TextField,
|
||||
Theme,
|
||||
Tooltip,
|
||||
Typography
|
||||
} from "@mui/material";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import ResponsiveDialog from "common/ResponsiveDialog";
|
||||
import { useMobile, useSnackbar } from "hooks";
|
||||
import { Field, HarvestPlan/*, Task*/ } from "models";
|
||||
import moment from "moment";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
import { useHarvestPlanAPI/*, useTaskAPI*/ } from "providers";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
//import TaskSettings from "tasks/TaskSettings";
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
close: (reload?: boolean, updatedPlan?: HarvestPlan) => void;
|
||||
field: Field;
|
||||
update?: boolean;
|
||||
plan?: HarvestPlan;
|
||||
}
|
||||
|
||||
const cropOptions = [
|
||||
{
|
||||
value: 2,
|
||||
label: "Barley"
|
||||
},
|
||||
{
|
||||
value: 4,
|
||||
label: "Canola"
|
||||
},
|
||||
{
|
||||
value: 5,
|
||||
label: "Corn"
|
||||
},
|
||||
{
|
||||
value: 6,
|
||||
label: "Oats"
|
||||
},
|
||||
{
|
||||
value: 7,
|
||||
label: "Peanuts"
|
||||
},
|
||||
{
|
||||
value: 8,
|
||||
label: "Rapeseed"
|
||||
},
|
||||
{
|
||||
value: 9,
|
||||
label: "Long Grain Rice"
|
||||
},
|
||||
{
|
||||
value: 10,
|
||||
label: "Medium Grain Rice"
|
||||
},
|
||||
{
|
||||
value: 11,
|
||||
label: "Short Grain Rice"
|
||||
},
|
||||
{
|
||||
value: 12,
|
||||
label: "Sorghum"
|
||||
},
|
||||
{
|
||||
value: 13,
|
||||
label: "Soybeans"
|
||||
},
|
||||
{
|
||||
value: 14,
|
||||
label: "Sunflower"
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: "Buckwheat"
|
||||
},
|
||||
{
|
||||
value: 15,
|
||||
label: "Durum Wheat"
|
||||
},
|
||||
{
|
||||
value: 16,
|
||||
label: "Hard Red Wheat"
|
||||
},
|
||||
{
|
||||
value: 17,
|
||||
label: "Unretted Flax"
|
||||
},
|
||||
{
|
||||
value: 18,
|
||||
label: "Dew Retted Flax"
|
||||
},
|
||||
{
|
||||
value: 19,
|
||||
label: "Yellow Peas"
|
||||
},
|
||||
{
|
||||
value: 20,
|
||||
label: "Robin Lentils"
|
||||
},
|
||||
{
|
||||
value: 21,
|
||||
label: "Redberry Lentils"
|
||||
},
|
||||
{
|
||||
value: 22,
|
||||
label: "Blaze Lentils"
|
||||
}
|
||||
];
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) => ({
|
||||
stepper: {
|
||||
padding: theme.spacing(0.5)
|
||||
},
|
||||
secondaryColor: {
|
||||
color: theme.palette.secondary.main
|
||||
},
|
||||
textSecondaryColor: {
|
||||
color: theme.palette.text.secondary
|
||||
},
|
||||
bottomSpacing: {
|
||||
marginBottom: theme.spacing(1)
|
||||
},
|
||||
tabs: {
|
||||
width: "100%",
|
||||
boxSizing: "border-box",
|
||||
flexShrink: 1
|
||||
},
|
||||
tabSmall: {
|
||||
width: "100%",
|
||||
boxSizing: "border-box",
|
||||
flexShrink: 1,
|
||||
minWidth: "48px"
|
||||
},
|
||||
tab: {
|
||||
width: "16%",
|
||||
minWidth: "16%"
|
||||
},
|
||||
buttonSpacing: {
|
||||
margin: 5
|
||||
}
|
||||
}));
|
||||
|
||||
const steps = ["Pre-Seeding", "Seeding", "Post-Seeding", "Harvest", "Fall"];
|
||||
|
||||
export default function HarvestSettings(props: Props) {
|
||||
const { open, plan, close, field, update } = props;
|
||||
const [planKey, setPlanKey] = useState<string>();
|
||||
const [newPlan, setNewPlan] = useState(HarvestPlan.create());
|
||||
const [activeStep, setActiveStep] = useState(0);
|
||||
const isMobile = useMobile();
|
||||
const classes = useStyles();
|
||||
//const [planTasks, setPlanTasks] = useState<Task[]>([]);
|
||||
//const taskAPI = useTaskAPI();
|
||||
const harvestPlanAPI = useHarvestPlanAPI();
|
||||
const [title, setTitle] = useState("");
|
||||
const [cropType, setCropType] = useState(0);
|
||||
const [customCropType, setCustomCropType] = useState("");
|
||||
const [useCustom, setUseCustom] = useState(false);
|
||||
const [variety, setVariety] = useState("");
|
||||
const [harvestYear, setHarvestYear] = useState(new Date().getFullYear());
|
||||
const [targetYield, setTargetYield] = useState(0);
|
||||
const [price, setPrice] = useState("");
|
||||
const [newTaskDialog, setNewTaskDialog] = useState(false);
|
||||
const [type, setType] = useState("");
|
||||
//const [taskToEdit, setTaskToEdit] = useState<Task>();
|
||||
const { openSnack } = useSnackbar();
|
||||
const [deleteOpen, setDeleteOpen] = useState(false);
|
||||
|
||||
// const loadTasks = useCallback(() => {
|
||||
// if (planKey) {
|
||||
// taskAPI.getMultiTasks([planKey]).then(resp => {
|
||||
// setPlanTasks(resp.data.tasks.map(t => Task.any(t)));
|
||||
// });
|
||||
// }
|
||||
// }, [taskAPI, planKey]);
|
||||
|
||||
// useEffect(() => {
|
||||
// loadTasks();
|
||||
// }, [loadTasks]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!plan || (plan && plan.permissions.includes(pond.Permission.PERMISSION_WRITE))) {
|
||||
setActiveStep(0);
|
||||
} else {
|
||||
setActiveStep(1);
|
||||
}
|
||||
setPlanKey(undefined);
|
||||
setTitle("");
|
||||
setVariety("");
|
||||
setHarvestYear(new Date().getFullYear());
|
||||
setTargetYield(0);
|
||||
setPrice("");
|
||||
if (update && plan) {
|
||||
setPlanKey(plan.key());
|
||||
setTitle(plan.settings.title);
|
||||
setCropType(plan.cropType());
|
||||
setCustomCropType(plan.customCropType());
|
||||
setVariety(plan.grainType());
|
||||
setHarvestYear(plan.harvestYear());
|
||||
setTargetYield(plan.yieldTarget());
|
||||
setPrice(plan.settings.bushelPrice.toFixed(2).toString());
|
||||
} else {
|
||||
setCropType(field.crop());
|
||||
setCustomCropType(field.customType());
|
||||
setUseCustom(field.crop() === pond.Grain.GRAIN_CUSTOM);
|
||||
}
|
||||
}, [plan, field, update]);
|
||||
|
||||
const closeDialog = (reload?: boolean, updatedPlan?: HarvestPlan) => {
|
||||
close(reload, updatedPlan);
|
||||
};
|
||||
|
||||
// const showTasks = (type: string) => {
|
||||
// let tasks: Task[] = [];
|
||||
// let costTotal = 0;
|
||||
// let secondaryCostTotal = 0;
|
||||
// planTasks.forEach(task => {
|
||||
// if (task.settings.type === type) {
|
||||
// tasks.push(task);
|
||||
// costTotal += task.cost();
|
||||
// secondaryCostTotal += task.secondaryCost();
|
||||
// }
|
||||
// });
|
||||
|
||||
// let frag = (
|
||||
// <React.Fragment>
|
||||
// <Table>
|
||||
// <TableHead>
|
||||
// <TableRow>
|
||||
// <TableCell>Title</TableCell>
|
||||
// <TableCell>Description</TableCell>
|
||||
// {!isMobile && <TableCell>Material Cost(acre)</TableCell>}
|
||||
// {!isMobile && <TableCell>Equipment Cost(acre)</TableCell>}
|
||||
// <TableCell>Total(acre)</TableCell>
|
||||
// {!isMobile && <TableCell>Start -End</TableCell>}
|
||||
// <TableCell>Edit</TableCell>
|
||||
// </TableRow>
|
||||
// </TableHead>
|
||||
// <TableBody>
|
||||
// {tasks.map((task, i) => (
|
||||
// <TableRow key={i}>
|
||||
// <TableCell>{task.title()}</TableCell>
|
||||
// <TableCell>{task.description()}</TableCell>
|
||||
// {!isMobile && <TableCell>${task.cost().toFixed(2)}</TableCell>}
|
||||
// {!isMobile && <TableCell>${task.secondaryCost().toFixed(2)}</TableCell>}
|
||||
// <TableCell>
|
||||
// ${(Math.round((task.cost() + task.secondaryCost()) * 100) / 100).toFixed(2)}
|
||||
// </TableCell>
|
||||
// {!isMobile && (
|
||||
// <TableCell>
|
||||
// {task.start()} {task.end()}
|
||||
// </TableCell>
|
||||
// )}
|
||||
// <TableCell>
|
||||
// <Button
|
||||
// color="primary"
|
||||
// onClick={() => {
|
||||
// setTaskToEdit(task);
|
||||
// setNewTaskDialog(true);
|
||||
// }}>
|
||||
// Edit
|
||||
// </Button>
|
||||
// </TableCell>
|
||||
// </TableRow>
|
||||
// ))}
|
||||
// </TableBody>
|
||||
// </Table>
|
||||
// <Box>
|
||||
// <Typography>
|
||||
// Total: $
|
||||
// {(Math.round((costTotal + secondaryCostTotal) * field.acres() * 100) / 100).toFixed(2)}
|
||||
// </Typography>
|
||||
// </Box>
|
||||
// </React.Fragment>
|
||||
// );
|
||||
// return frag;
|
||||
// };
|
||||
|
||||
const savePlan = () => {
|
||||
let newPlanSettings = pond.HarvestPlanSettings.create({
|
||||
title: title,
|
||||
field: field.key(),
|
||||
createDate: moment.now(),
|
||||
cropType: cropType,
|
||||
grainType: variety,
|
||||
harvestYear: harvestYear,
|
||||
yieldTarget: targetYield,
|
||||
bushelPrice: !isNaN(parseFloat(price)) ? Math.round(parseFloat(price) * 100) / 100 : 0
|
||||
});
|
||||
setNewPlan(HarvestPlan.create(pond.HarvestPlan.fromObject({ settings: newPlanSettings })));
|
||||
|
||||
harvestPlanAPI.addHarvestPlan(newPlanSettings).then(resp => {
|
||||
setPlanKey(resp.data.harvestPlan);
|
||||
openSnack("Plan Created");
|
||||
});
|
||||
};
|
||||
|
||||
const updatePlan = () => {
|
||||
//material costs per acre
|
||||
let preseedMatCost = 0;
|
||||
let seedMatCost = 0;
|
||||
let postseedMatCost = 0;
|
||||
let harvestMatCost = 0;
|
||||
let fallMatCost = 0;
|
||||
|
||||
//equipment costs per acre
|
||||
let preseedEqCost = 0;
|
||||
let seedEqCost = 0;
|
||||
let postseedEqCost = 0;
|
||||
let harvestEqCost = 0;
|
||||
let fallEqCost = 0;
|
||||
// planTasks.forEach(task => {
|
||||
// switch (task.settings.type) {
|
||||
// case "preseed":
|
||||
// preseedMatCost += task.cost();
|
||||
// preseedEqCost += task.secondaryCost();
|
||||
// break;
|
||||
// case "seed":
|
||||
// seedMatCost += task.cost();
|
||||
// seedEqCost += task.secondaryCost();
|
||||
// break;
|
||||
// case "postseed":
|
||||
// postseedMatCost += task.cost();
|
||||
// postseedEqCost += task.secondaryCost();
|
||||
// break;
|
||||
// case "harvest":
|
||||
// harvestMatCost += task.cost();
|
||||
// harvestEqCost += task.secondaryCost();
|
||||
// break;
|
||||
// default:
|
||||
// fallMatCost += task.cost();
|
||||
// fallEqCost += task.secondaryCost();
|
||||
// break;
|
||||
// }
|
||||
// });
|
||||
let tempPlan = plan ?? newPlan;
|
||||
tempPlan.settings.cropType = cropType;
|
||||
tempPlan.settings.field = field.key();
|
||||
tempPlan.settings.title = title;
|
||||
tempPlan.settings.grainType = variety;
|
||||
tempPlan.settings.harvestYear = harvestYear;
|
||||
tempPlan.settings.yieldTarget = targetYield;
|
||||
tempPlan.settings.bushelPrice = !isNaN(parseFloat(price))
|
||||
? Math.round(parseFloat(price) * 100) / 100
|
||||
: 0;
|
||||
tempPlan.settings.preSeedMaterials = preseedMatCost;
|
||||
tempPlan.settings.preSeedEquipment = preseedEqCost;
|
||||
tempPlan.settings.seedMaterials = seedMatCost;
|
||||
tempPlan.settings.seedEquipment = seedEqCost;
|
||||
tempPlan.settings.postSeedMaterials = postseedMatCost;
|
||||
tempPlan.settings.postSeedEquipment = postseedEqCost;
|
||||
tempPlan.settings.harvestMaterials = harvestMatCost;
|
||||
tempPlan.settings.harvestEquipment = harvestEqCost;
|
||||
tempPlan.settings.fallMaterials = fallMatCost;
|
||||
tempPlan.settings.fallEquipment = fallEqCost;
|
||||
if (planKey) {
|
||||
harvestPlanAPI
|
||||
.updateHarvestPlan(planKey, tempPlan.settings)
|
||||
.then(resp => openSnack("Plan Updated"));
|
||||
}
|
||||
closeDialog(true, tempPlan);
|
||||
};
|
||||
|
||||
const deletePlan = () => {
|
||||
if (planKey) {
|
||||
harvestPlanAPI.removeHarvestPlan(planKey).then(resp => {
|
||||
openSnack("Plan Deleted");
|
||||
});
|
||||
}
|
||||
setDeleteOpen(false);
|
||||
closeDialog(true);
|
||||
};
|
||||
|
||||
const handleNextStep = () => {
|
||||
setActiveStep(prevStep => prevStep + 1);
|
||||
};
|
||||
const handleBack = () => {
|
||||
setActiveStep(prevActiveStep => prevActiveStep - 1);
|
||||
};
|
||||
|
||||
const stepper = () => {
|
||||
return (
|
||||
<Box display="flex" justifyContent="center" width="100%">
|
||||
<Tabs
|
||||
value={activeStep}
|
||||
onChange={(_, value) => setActiveStep(value)}
|
||||
indicatorColor="primary"
|
||||
textColor="primary"
|
||||
variant="fullWidth"
|
||||
aria-label="bin tabs"
|
||||
classes={{ root: classes.tabs }}>
|
||||
<Tab
|
||||
key={0}
|
||||
classes={{ root: classes.tab }}
|
||||
disabled={
|
||||
!planKey || !(plan && plan.permissions.includes(pond.Permission.PERMISSION_WRITE))
|
||||
}
|
||||
label={"General"}
|
||||
aria-label={"general"}
|
||||
/>
|
||||
{steps.map((label, i) => (
|
||||
<Tab
|
||||
key={i + 1}
|
||||
classes={{ root: classes.tab }}
|
||||
disabled={!planKey}
|
||||
label={label}
|
||||
aria-label={label}
|
||||
/>
|
||||
))}
|
||||
</Tabs>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const general = () => {
|
||||
return (
|
||||
<Box>
|
||||
<TextField
|
||||
fullWidth
|
||||
margin="normal"
|
||||
type="text"
|
||||
label="Title"
|
||||
value={title}
|
||||
onChange={e => setTitle(e.target.value)}
|
||||
/>
|
||||
{useCustom ? (
|
||||
<TextField
|
||||
margin="normal"
|
||||
fullWidth
|
||||
label="Crop"
|
||||
value={customCropType}
|
||||
onChange={e => setCustomCropType(e.target.value)}
|
||||
color="primary"
|
||||
/>
|
||||
) : (
|
||||
<TextField
|
||||
margin="normal"
|
||||
fullWidth
|
||||
select
|
||||
label="Crop"
|
||||
value={cropType}
|
||||
onChange={e => setCropType(+e.target.value)}
|
||||
color="primary">
|
||||
{cropOptions.map(option => (
|
||||
<MenuItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
)}
|
||||
<TextField
|
||||
fullWidth
|
||||
margin="normal"
|
||||
type="text"
|
||||
label="Variety"
|
||||
value={variety}
|
||||
onChange={e => setVariety(e.target.value)}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
margin="normal"
|
||||
type="number"
|
||||
label="Year"
|
||||
value={harvestYear}
|
||||
onChange={e => {
|
||||
!isNaN(+e.target.value) && setHarvestYear(+e.target.value);
|
||||
}}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
margin="normal"
|
||||
type="number"
|
||||
label="Target Yield per acre"
|
||||
value={targetYield}
|
||||
onChange={e => {
|
||||
!isNaN(+e.target.value) && setTargetYield(+e.target.value);
|
||||
}}
|
||||
/>
|
||||
<TextField
|
||||
margin="dense"
|
||||
id="bushPrice"
|
||||
label="Estimated sale price per bushel"
|
||||
type="text"
|
||||
fullWidth
|
||||
helperText="Must be a valid number*"
|
||||
value={price}
|
||||
error={isNaN(+price) && price !== ""}
|
||||
InputProps={{
|
||||
startAdornment: <InputAdornment position="start">$</InputAdornment>
|
||||
}}
|
||||
onChange={e => {
|
||||
setPrice(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const preSeed = () => {
|
||||
return (
|
||||
<Box textAlign="center">
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
setType("preseed");
|
||||
setNewTaskDialog(true);
|
||||
}}>
|
||||
Add Crop Activity
|
||||
</Button>
|
||||
<Typography>Planned Activities</Typography>
|
||||
{/* {showTasks("preseed")} */}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const seed = () => {
|
||||
return (
|
||||
<Box textAlign="center">
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
setType("seed");
|
||||
setNewTaskDialog(true);
|
||||
}}>
|
||||
Add Crop Activity
|
||||
</Button>
|
||||
<Typography>Planned Activities</Typography>
|
||||
{/* {showTasks("seed")} */}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const postSeed = () => {
|
||||
return (
|
||||
<Box textAlign="center">
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
setType("postseed");
|
||||
setNewTaskDialog(true);
|
||||
}}>
|
||||
Add Crop Activity
|
||||
</Button>
|
||||
<Typography>Planned Activities</Typography>
|
||||
{/* {showTasks("postseed")} */}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const harvest = () => {
|
||||
return (
|
||||
<Box textAlign="center">
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
setType("harvest");
|
||||
setNewTaskDialog(true);
|
||||
}}>
|
||||
Add Crop Activity
|
||||
</Button>
|
||||
<Typography>Planned Activities</Typography>
|
||||
{/* {showTasks("harvest")} */}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const fall = () => {
|
||||
return (
|
||||
<Box textAlign="center">
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
setType("fall");
|
||||
setNewTaskDialog(true);
|
||||
}}>
|
||||
Add Crop Activity
|
||||
</Button>
|
||||
<Typography>Planned Activities</Typography>
|
||||
{/* {showTasks("fall")} */}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const stepperContent = (step: number) => {
|
||||
switch (step) {
|
||||
case 1:
|
||||
return preSeed();
|
||||
case 2:
|
||||
return seed();
|
||||
case 3:
|
||||
return postSeed();
|
||||
case 4:
|
||||
return harvest();
|
||||
case 5:
|
||||
return fall();
|
||||
default:
|
||||
return general();
|
||||
}
|
||||
};
|
||||
|
||||
const deleteDialog = () => {
|
||||
return (
|
||||
<ResponsiveDialog open={deleteOpen}>
|
||||
<DialogContent>
|
||||
Are you sure you wish to delete plan? This action is irreversible.
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setDeleteOpen(false)}>Cancel</Button>
|
||||
<Button onClick={deletePlan}>
|
||||
<Typography color="error">Delete</Typography>
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</ResponsiveDialog>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<ResponsiveDialog open={open} onClose={() => closeDialog()} maxWidth="lg">
|
||||
<DialogTitle>Create New Crop Plan</DialogTitle>
|
||||
<DialogContent>
|
||||
{stepper()}
|
||||
{stepperContent(activeStep)}
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Grid container direction="row">
|
||||
<Grid>
|
||||
{planKey && plan && field.permissions.includes(pond.Permission.PERMISSION_WRITE) && (
|
||||
<Button
|
||||
variant="contained"
|
||||
style={{ color: "black", backgroundColor: "red" }}
|
||||
onClick={() => setDeleteOpen(true)}>
|
||||
Delete
|
||||
</Button>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid>
|
||||
<Button
|
||||
className={classes.buttonSpacing}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => closeDialog()}>
|
||||
Cancel
|
||||
</Button>
|
||||
{activeStep === 0 && !planKey && (
|
||||
<React.Fragment>
|
||||
<Button
|
||||
className={classes.buttonSpacing}
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
savePlan();
|
||||
}}
|
||||
color="primary">
|
||||
Create Plan
|
||||
</Button>
|
||||
</React.Fragment>
|
||||
)}
|
||||
{activeStep > 0 && (
|
||||
<Button
|
||||
className={classes.buttonSpacing}
|
||||
variant="contained"
|
||||
onClick={handleBack}
|
||||
color="primary">
|
||||
Back
|
||||
</Button>
|
||||
)}
|
||||
{activeStep < 5 && (activeStep > 0 || planKey) && (
|
||||
<Button
|
||||
className={classes.buttonSpacing}
|
||||
variant="contained"
|
||||
onClick={handleNextStep}
|
||||
color="primary">
|
||||
Next
|
||||
</Button>
|
||||
)}
|
||||
{(planKey || activeStep > 0) && (
|
||||
<Tooltip title="Finish Harvest Plan">
|
||||
<Button
|
||||
className={classes.buttonSpacing}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={updatePlan}>
|
||||
Confirm
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</DialogActions>
|
||||
</ResponsiveDialog>
|
||||
{deleteDialog()}
|
||||
{/* <TaskSettings
|
||||
task={taskToEdit}
|
||||
hasCost
|
||||
costTitle="Material Cost(acres)"
|
||||
secondaryCostTitle="Equipment Cost(acre)"
|
||||
objectKey={planKey}
|
||||
type={type}
|
||||
open={newTaskDialog}
|
||||
onClose={r => {
|
||||
setType("");
|
||||
setTaskToEdit(undefined);
|
||||
setNewTaskDialog(false);
|
||||
if (r) {
|
||||
loadTasks();
|
||||
}
|
||||
}}
|
||||
/> */}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue