added fields list page, left out the harvest plan table as i would like to re-do and this is a good oppurtunity (if people want it)
This commit is contained in:
parent
cdad408152
commit
2816884771
5 changed files with 228 additions and 0 deletions
153
src/field/FieldList.tsx
Normal file
153
src/field/FieldList.tsx
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Paper,
|
||||||
|
Tab,
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableContainer,
|
||||||
|
TableHead,
|
||||||
|
TableRow,
|
||||||
|
Tabs
|
||||||
|
} from "@mui/material";
|
||||||
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
|
import { useMobile, useSnackbar } from "hooks";
|
||||||
|
import { useGlobalState, useFieldAPI } from "providers";
|
||||||
|
//import HarvestTable from "harvestPlan/HarvestTable";
|
||||||
|
import { Field } from "models";
|
||||||
|
import GrainDescriber from "grain/GrainDescriber";
|
||||||
|
import { pond } from "protobuf-ts/pond";
|
||||||
|
import HarvestSettings from "harvestPlan/HarvestSettings";
|
||||||
|
|
||||||
|
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}`}
|
||||||
|
style={{ height: "94%" }}
|
||||||
|
{...other}>
|
||||||
|
{value === index && <React.Fragment>{children}</React.Fragment>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function FieldList() {
|
||||||
|
const [{ as }] = useGlobalState();
|
||||||
|
const isMobile = useMobile();
|
||||||
|
const fieldAPI = useFieldAPI();
|
||||||
|
const { openSnack } = useSnackbar();
|
||||||
|
const [fields, setFields] = useState<Field[]>([]);
|
||||||
|
const [value, setValue] = React.useState(0);
|
||||||
|
const [openHarvestSettings, setOpenHarvestSettings] = useState(false);
|
||||||
|
const [fieldForPlan, setFieldForPlan] = useState(Field.create());
|
||||||
|
|
||||||
|
const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => {
|
||||||
|
setValue(newValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadFields = useCallback(() => {
|
||||||
|
fieldAPI
|
||||||
|
.listFields(500, 0, "asc", "fieldName", undefined, as)
|
||||||
|
.then(resp => {
|
||||||
|
setFields(resp.data.fields.map(f => Field.any(f)));
|
||||||
|
// resp.data.fields.forEach(f => {
|
||||||
|
// if (f.settings) {
|
||||||
|
// fieldAPI.updateField(f.settings.key, f.settings);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
openSnack("Failed to load Field Mapping");
|
||||||
|
});
|
||||||
|
}, [fieldAPI, as, openSnack]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadFields();
|
||||||
|
}, [loadFields]);
|
||||||
|
|
||||||
|
const calcTotalAcres = () => {
|
||||||
|
let totalAcres = 0;
|
||||||
|
fields.forEach(field => {
|
||||||
|
totalAcres += field.acres();
|
||||||
|
});
|
||||||
|
totalAcres = Math.round(totalAcres);
|
||||||
|
return totalAcres;
|
||||||
|
};
|
||||||
|
|
||||||
|
const listFieldsInfo = fields.map((field, index) => (
|
||||||
|
<TableRow key={index}>
|
||||||
|
<TableCell>{field.fieldName()}</TableCell>
|
||||||
|
<TableCell>{field.landLoc()}</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{field.crop() === pond.Grain.GRAIN_CUSTOM
|
||||||
|
? field.customType()
|
||||||
|
: GrainDescriber(field.crop()).name}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>{field.calculateAcres()}</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{field.permissions.includes(pond.Permission.PERMISSION_WRITE) && (
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => {
|
||||||
|
setFieldForPlan(field);
|
||||||
|
setOpenHarvestSettings(true);
|
||||||
|
}}>
|
||||||
|
New Crop Plan
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Tabs
|
||||||
|
value={value}
|
||||||
|
onChange={handleChange}
|
||||||
|
TabIndicatorProps={{ style: { background: "rgba(255,255,0,255)" } }}>
|
||||||
|
<Tab label="Field Overview" />
|
||||||
|
{/* {!isMobile && <Tab label="Current Harvest Plans" />}
|
||||||
|
{!isMobile && <Tab label="Plan History" />} */}
|
||||||
|
</Tabs>
|
||||||
|
<TabPanelMine index={0} value={value}>
|
||||||
|
<TableContainer component={Paper}>
|
||||||
|
<Table style={{ minWidth: 1000 }}>
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>Field Name</TableCell>
|
||||||
|
<TableCell>Land Location</TableCell>
|
||||||
|
<TableCell>Main Crop Type</TableCell>
|
||||||
|
<TableCell>Acres ({calcTotalAcres()} Total)</TableCell>
|
||||||
|
<TableCell>Create New Plan</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>{listFieldsInfo}</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
</TabPanelMine>
|
||||||
|
{/* need to re-factor the harvest table file before restoring these functions */}
|
||||||
|
{/* <TabPanelMine index={1} value={value}>
|
||||||
|
<HarvestTable fields={fields} display="current" />
|
||||||
|
</TabPanelMine>
|
||||||
|
<TabPanelMine index={2} value={value}>
|
||||||
|
<HarvestTable fields={fields} display="history" />
|
||||||
|
</TabPanelMine> */}
|
||||||
|
<HarvestSettings
|
||||||
|
open={openHarvestSettings}
|
||||||
|
close={() => setOpenHarvestSettings(false)}
|
||||||
|
field={fieldForPlan}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -21,6 +21,7 @@ import Ventilation from "pages/VentEditor";
|
||||||
// import Transactions from "pages/Transactions";
|
// import Transactions from "pages/Transactions";
|
||||||
// import BinCableEstimator from "pages/BinCableEstimator";
|
// import BinCableEstimator from "pages/BinCableEstimator";
|
||||||
import { useGlobalState } from "providers";
|
import { useGlobalState } from "providers";
|
||||||
|
// import Fields from "pages/Fields";
|
||||||
//import Site from "pages/Site";
|
//import Site from "pages/Site";
|
||||||
|
|
||||||
const DeviceHistory = lazy(() => import("pages/DeviceHistory"));
|
const DeviceHistory = lazy(() => import("pages/DeviceHistory"));
|
||||||
|
|
@ -49,6 +50,7 @@ const Tasks = lazy(() => import("pages/Tasks"));
|
||||||
const Transactions = lazy(() => import("pages/Transactions"));
|
const Transactions = lazy(() => import("pages/Transactions"));
|
||||||
const BinCableEstimator = lazy(() => import("pages/BinCableEstimator"));
|
const BinCableEstimator = lazy(() => import("pages/BinCableEstimator"));
|
||||||
const APIDocs = lazy(() => import("pages/APIDocs"));
|
const APIDocs = lazy(() => import("pages/APIDocs"));
|
||||||
|
const Fields = lazy(()=> import("pages/Fields"));
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
toggleTheme: () => void;
|
toggleTheme: () => void;
|
||||||
|
|
@ -326,6 +328,7 @@ export default function Router(props: Props) {
|
||||||
{user.hasFeature("developer") &&
|
{user.hasFeature("developer") &&
|
||||||
<Route path="api" element={<APIDocs />} />
|
<Route path="api" element={<APIDocs />} />
|
||||||
}
|
}
|
||||||
|
<Route path="fields" element={<Fields />} />
|
||||||
|
|
||||||
{/* Map pages */}
|
{/* Map pages */}
|
||||||
<Route path="visualFarm" element={<FieldMap />} />
|
<Route path="visualFarm" element={<FieldMap />} />
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ import { getThemeType } from "theme";
|
||||||
import ObjectHeaterIcon from "products/Construction/ObjectHeaterIcon";
|
import ObjectHeaterIcon from "products/Construction/ObjectHeaterIcon";
|
||||||
import TasksIcon from "products/Construction/TasksIcon";
|
import TasksIcon from "products/Construction/TasksIcon";
|
||||||
import CableIcon from "products/Bindapt/CableIcon";
|
import CableIcon from "products/Bindapt/CableIcon";
|
||||||
|
import FieldListIcon from "products/AgIcons/FieldList";
|
||||||
|
|
||||||
const drawerWidth = 230;
|
const drawerWidth = 230;
|
||||||
|
|
||||||
|
|
@ -243,6 +244,20 @@ export default function SideNavigator(props: Props) {
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
|
{(isAg || user.hasFeature("admin")) && (
|
||||||
|
<Tooltip title="My Fields" placement="right">
|
||||||
|
<ListItemButton
|
||||||
|
id="tour-field-list"
|
||||||
|
onClick={() => goTo("/fields")}
|
||||||
|
classes={getClasses("/fields")}
|
||||||
|
>
|
||||||
|
<ListItemIcon>
|
||||||
|
<FieldListIcon />
|
||||||
|
</ListItemIcon>
|
||||||
|
{open && <ListItemText primary="My Fields" />}
|
||||||
|
</ListItemButton>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
{(isMiPCA || user.hasFeature("admin")) && (
|
{(isMiPCA || user.hasFeature("admin")) && (
|
||||||
<Tooltip title="Aviation Map" placement="right">
|
<Tooltip title="Aviation Map" placement="right">
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
|
|
|
||||||
14
src/pages/Fields.tsx
Normal file
14
src/pages/Fields.tsx
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import React from "react";
|
||||||
|
import PageContainer from "./PageContainer";
|
||||||
|
import FieldList from "field/FieldList";
|
||||||
|
import { Box } from "@mui/material";
|
||||||
|
|
||||||
|
export default function Fields() {
|
||||||
|
return (
|
||||||
|
<PageContainer>
|
||||||
|
<Box padding={2}>
|
||||||
|
<FieldList />
|
||||||
|
</Box>
|
||||||
|
</PageContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
43
src/pbHelpers/HarvestPlan.ts
Normal file
43
src/pbHelpers/HarvestPlan.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
import { pond } from "protobuf-ts/pond";
|
||||||
|
import { capitalize } from "utils/strings";
|
||||||
|
import { or } from "utils";
|
||||||
|
import GrainDescriber from "grain/GrainDescriber";
|
||||||
|
|
||||||
|
//TODO handle new keys for pre/post seed
|
||||||
|
const keyTranslator = new Map<keyof pond.HarvestPlanSettings, string>([
|
||||||
|
["createDate", "Create Date"],
|
||||||
|
["actualYield", "Actual Yield"],
|
||||||
|
["cropType", "Crop Type"],
|
||||||
|
["field", "Field"],
|
||||||
|
["grainType", "grain"],
|
||||||
|
["yieldTarget", "Yield Target"],
|
||||||
|
["bushelPrice", "Price per Bushel"],
|
||||||
|
["preSeedMaterials", "Pre-seed Material Cost (acre)"],
|
||||||
|
["preSeedEquipment", "Pre-seed Equipment Cost (acre)"]
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Keys will be stringified by default if not found in the keyTranslator
|
||||||
|
export function TranslateKey(key: keyof pond.HarvestPlanSettings): string {
|
||||||
|
let translatedKey = keyTranslator.get(key);
|
||||||
|
return translatedKey ? translatedKey : capitalize(key.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
const valueTranslator = new Map<
|
||||||
|
keyof pond.HarvestPlanSettings,
|
||||||
|
(device: pond.HarvestPlanSettings) => string
|
||||||
|
>([
|
||||||
|
["createDate", device => new Date(device.createDate).toString()],
|
||||||
|
["cropType", device => GrainDescriber(device.cropType).name]
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Values will be stringified by default if its key is not found in the valueTranslator
|
||||||
|
export function TranslateValue(
|
||||||
|
key: keyof pond.HarvestPlanSettings,
|
||||||
|
device: pond.HarvestPlanSettings
|
||||||
|
) {
|
||||||
|
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) : "";
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue