From 28168847719305074e041b31af885d55c97c5f15 Mon Sep 17 00:00:00 2001 From: csawatzky Date: Fri, 11 Apr 2025 10:57:12 -0600 Subject: [PATCH] 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) --- src/field/FieldList.tsx | 153 +++++++++++++++++++++++++++++++ src/navigation/Router.tsx | 3 + src/navigation/SideNavigator.tsx | 15 +++ src/pages/Fields.tsx | 14 +++ src/pbHelpers/HarvestPlan.ts | 43 +++++++++ 5 files changed, 228 insertions(+) create mode 100644 src/field/FieldList.tsx create mode 100644 src/pages/Fields.tsx create mode 100644 src/pbHelpers/HarvestPlan.ts diff --git a/src/field/FieldList.tsx b/src/field/FieldList.tsx new file mode 100644 index 0000000..6243ce3 --- /dev/null +++ b/src/field/FieldList.tsx @@ -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 ( + + ); +} + +export default function FieldList() { + const [{ as }] = useGlobalState(); + const isMobile = useMobile(); + const fieldAPI = useFieldAPI(); + const { openSnack } = useSnackbar(); + const [fields, setFields] = useState([]); + 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) => ( + + {field.fieldName()} + {field.landLoc()} + + {field.crop() === pond.Grain.GRAIN_CUSTOM + ? field.customType() + : GrainDescriber(field.crop()).name} + + {field.calculateAcres()} + + {field.permissions.includes(pond.Permission.PERMISSION_WRITE) && ( + + )} + + + )); + + return ( + + + + {/* {!isMobile && } + {!isMobile && } */} + + + + + + + Field Name + Land Location + Main Crop Type + Acres ({calcTotalAcres()} Total) + Create New Plan + + + {listFieldsInfo} +
+
+
+ {/* need to re-factor the harvest table file before restoring these functions */} + {/* + + + + + */} + setOpenHarvestSettings(false)} + field={fieldForPlan} + /> +
+ ); +} \ No newline at end of file diff --git a/src/navigation/Router.tsx b/src/navigation/Router.tsx index 377fd7c..a576a50 100644 --- a/src/navigation/Router.tsx +++ b/src/navigation/Router.tsx @@ -21,6 +21,7 @@ import Ventilation from "pages/VentEditor"; // import Transactions from "pages/Transactions"; // import BinCableEstimator from "pages/BinCableEstimator"; import { useGlobalState } from "providers"; +// import Fields from "pages/Fields"; //import Site from "pages/Site"; const DeviceHistory = lazy(() => import("pages/DeviceHistory")); @@ -49,6 +50,7 @@ const Tasks = lazy(() => import("pages/Tasks")); const Transactions = lazy(() => import("pages/Transactions")); const BinCableEstimator = lazy(() => import("pages/BinCableEstimator")); const APIDocs = lazy(() => import("pages/APIDocs")); +const Fields = lazy(()=> import("pages/Fields")); interface Props { toggleTheme: () => void; @@ -326,6 +328,7 @@ export default function Router(props: Props) { {user.hasFeature("developer") && } /> } + } /> {/* Map pages */} } /> diff --git a/src/navigation/SideNavigator.tsx b/src/navigation/SideNavigator.tsx index 36c5be5..007b345 100644 --- a/src/navigation/SideNavigator.tsx +++ b/src/navigation/SideNavigator.tsx @@ -41,6 +41,7 @@ import { getThemeType } from "theme"; import ObjectHeaterIcon from "products/Construction/ObjectHeaterIcon"; import TasksIcon from "products/Construction/TasksIcon"; import CableIcon from "products/Bindapt/CableIcon"; +import FieldListIcon from "products/AgIcons/FieldList"; const drawerWidth = 230; @@ -243,6 +244,20 @@ export default function SideNavigator(props: Props) { )} + {(isAg || user.hasFeature("admin")) && ( + + goTo("/fields")} + classes={getClasses("/fields")} + > + + + + {open && } + + + )} {(isMiPCA || user.hasFeature("admin")) && ( + + + + + ); +} \ No newline at end of file diff --git a/src/pbHelpers/HarvestPlan.ts b/src/pbHelpers/HarvestPlan.ts new file mode 100644 index 0000000..70e2979 --- /dev/null +++ b/src/pbHelpers/HarvestPlan.ts @@ -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([ + ["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) : ""; +}