diff --git a/src/grain/CustomGrainForm.tsx b/src/grain/CustomGrainForm.tsx
index 2a0379a..905931c 100644
--- a/src/grain/CustomGrainForm.tsx
+++ b/src/grain/CustomGrainForm.tsx
@@ -1,7 +1,7 @@
import {MenuItem, TextField } from "@mui/material"
import { cloneDeep } from "lodash"
import { pond } from "protobuf-ts/pond"
-import React, { useEffect, useState } from "react"
+import React, { useEffect, useRef, useState } from "react"
interface Props {
grainSettings?: pond.GrainSettings
@@ -19,6 +19,7 @@ export default function CustomGrainForm(props: Props) {
const [constantC, setConstantC] = useState("0")
const [kgPerBushel, setKgPerBushel] = useState("0")
const [bushelsPerTonne, setBushelsPerTonne] = useState("0")
+ const valid = useRef(false)
useEffect(()=>{
if(grainSettings){
@@ -34,20 +35,20 @@ export default function CustomGrainForm(props: Props) {
}
},[grainSettings])
- const invalid = () => {
- if (name === "") return true
- if (group === "") return true
- if (isNaN(parseFloat(constantA))) return true
- if (isNaN(parseFloat(constantB))) return true
- if (isNaN(parseFloat(constantA))) return true
- if (isNaN(parseFloat(kgPerBushel))) return true
- if (isNaN(parseFloat(bushelsPerTonne))) return true
- return false
+ const validate = (name: string, group: string, constA: string, constB: string, constC: string, kgPerBushel: string, bushelsPerTonne: string) => {
+ if (name === "") return false
+ if (group === "") return false
+ if (isNaN(parseFloat(constA))) return false
+ if (isNaN(parseFloat(constB))) return false
+ if (isNaN(parseFloat(constC))) return false
+ if (isNaN(parseFloat(kgPerBushel))) return false
+ if (isNaN(parseFloat(bushelsPerTonne))) return false
+ return true
}
const settingsChanged = (newSettings: pond.GrainSettings) => {
setNewGrainSettings(newSettings)
- onGrainSettingsChange(newSettings, !invalid())
+ onGrainSettingsChange(newSettings, valid.current)
}
const grainForm = () => {
@@ -63,6 +64,7 @@ export default function CustomGrainForm(props: Props) {
setName(name)
let settings = cloneDeep(newGrainSettings)
settings.name = name
+ valid.current = validate(name, group, constantA, constantB, constantC, kgPerBushel, bushelsPerTonne)
settingsChanged(settings)
}}/>
@@ -117,11 +121,12 @@ export default function CustomGrainForm(props: Props) {
onChange={(e) => {
let val = e.target.value
setConstantA(val)
+ valid.current = validate(name, group, val, constantB, constantC, kgPerBushel, bushelsPerTonne)
+ let settings = cloneDeep(newGrainSettings)
if(!isNaN(parseFloat(val))){
- let settings = cloneDeep(newGrainSettings)
settings.a = parseFloat(val)
- settingsChanged(settings)
}
+ settingsChanged(settings)
}}
label="Constant A"/>
{
let val = e.target.value
setConstantB(val)
+ valid.current = validate(name, group, constantA, val, constantC, kgPerBushel, bushelsPerTonne)
+ let settings = cloneDeep(newGrainSettings)
if(!isNaN(parseFloat(val))){
- let settings = cloneDeep(newGrainSettings)
settings.b = parseFloat(val)
- settingsChanged(settings)
}
+ settingsChanged(settings)
}}
label="Constant B"/>
{
let val = e.target.value
setConstantC(val)
+ valid.current = validate(name, group, constantA, constantB, val, kgPerBushel, bushelsPerTonne)
+ let settings = cloneDeep(newGrainSettings)
if(!isNaN(parseFloat(val))){
- let settings = cloneDeep(newGrainSettings)
settings.c = parseFloat(val)
- settingsChanged(settings)
}
+ settingsChanged(settings)
}}
label="Constant C"/>
{
let val = e.target.value
setKgPerBushel(val)
+ valid.current = validate(name, group, constantA, constantB, constantC, val, bushelsPerTonne)
+ let settings = cloneDeep(newGrainSettings)
if(!isNaN(parseFloat(val))){
- let settings = cloneDeep(newGrainSettings)
settings.kgPerBushel = parseFloat(val)
- settingsChanged(settings)
}
+ settingsChanged(settings)
}}
label="kg per Bushel"/>
{
let val = e.target.value
setBushelsPerTonne(val)
+ valid.current = validate(name, group, constantA, constantB, constantC, kgPerBushel, val)
+ let settings = cloneDeep(newGrainSettings)
if(!isNaN(parseFloat(val))){
- let settings = cloneDeep(newGrainSettings)
settings.bushelsPerTonne = parseFloat(val)
- settingsChanged(settings)
}
+ settingsChanged(settings)
}}
label="Bushels per Tonne"/>
diff --git a/src/pages/Grains.tsx b/src/pages/Grains.tsx
index 59fbead..2ff47eb 100644
--- a/src/pages/Grains.tsx
+++ b/src/pages/Grains.tsx
@@ -1,7 +1,7 @@
import { useCallback, useEffect, useState } from "react";
import PageContainer from "./PageContainer";
import CustomGrainForm from "grain/CustomGrainForm";
-import { Box, Button, DialogActions, DialogContent, DialogTitle, Grid2, Typography } from "@mui/material";
+import { Accordion, AccordionDetails, AccordionSummary, Box, Button, DialogActions, DialogContent, DialogTitle, Divider, Grid2, IconButton, Typography } from "@mui/material";
import { pond } from "protobuf-ts/pond";
import ButtonGroup from "common/ButtonGroup";
import { useMobile, useSnackbar, useUserAPI } from "hooks";
@@ -11,6 +11,8 @@ import { GetGrainExtensionMap, GetValidGrainExtensions, GrainExtension } from "g
import ResponsiveDialog from "common/ResponsiveDialog";
import { useGlobalState } from "providers";
import { Scope } from "models";
+import React from "react";
+import { AddCircle, ExpandMore } from "@mui/icons-material";
export default function Grains() {
const [currentCustomGrain, setCurrentCustomGrain] = useState()
@@ -18,7 +20,7 @@ export default function Grains() {
const [selectedGrain, setSelectedGrain] = useState()
const [displayCustom, setDisplayCustom] = useState(false)
const { openSnack } = useSnackbar()
- const [tableSize, setTableSize] = useState(5)
+ const [tableSize, setTableSize] = useState(10)
const [tablePage, setTablePage] = useState(0)
const [tableTotal, setTableTotal] = useState(0)
const grainAPI = useGrainAPI()
@@ -29,7 +31,11 @@ export default function Grains() {
const [openUpdateConfirmation, setOpenUpdateConfirmation] = useState(false)
const [grainObjectTableData, setGrainObjectTableData] = useState([])
const [supportedGrainTableData, setSupportedGrainTableData] = useState([])
+ // const [selectedGrainExtension, setSelectedGrainExtension] = useState()
+ const [accordionKey, setAccordionKey] = useState("allClosed")
+ const [openNewGrainDialog, setOpenNewGrainDialog] = useState(false)
const isMobile = useMobile()
+ const extraLoad = 10 //the amount of more to load on the mobile view when load more is clicked
//function to load the first set of custom grains
const loadCustomGrain = useCallback(() => {
@@ -92,8 +98,25 @@ export default function Grains() {
return (
rows={grainObjectTableData}
+ noDataMessage="No Grain Types Found. Add a new custom grain to see them here"
+ loadMore={() => {
+ setTableSize(tableSize + extraLoad)
+ }}
+ renderMobile={(row) => {
+ //close it if it is already open
+ if(accordionKey === row.key){
+ setAccordionKey("allClosed")
+ }else{
+ setAccordionKey(row.key)
+ }
+ setCurrentCustomGrain(row.settings ?? pond.GrainSettings.create())
+ }}>
+ }>{row.name}
+
+ {customGrainDisplay()}
+
+ }
onRowClick={(grainObj) => {
- setCurrentCustomGrain(grainObj.settings ?? pond.GrainSettings.create())
setSelectedGrain(grainObj)
setFormValid(true)
/** if they are viewin as a team leave the permissions alone, if not then use the users permissions to the grain type,
@@ -108,7 +131,7 @@ export default function Grains() {
columns={[
{
title: "Name",
- render: row =>
+ render: row =>
{row.name}
@@ -116,7 +139,7 @@ export default function Grains() {
},
{
title: "Group",
- render: row =>
+ render: row =>
{row.settings?.group}
@@ -124,7 +147,7 @@ export default function Grains() {
},
{
title: "Equation",
- render: row =>
+ render: row =>
{equationName(row.settings?.equation ?? 0)}
@@ -150,11 +173,21 @@ export default function Grains() {
rows={supportedGrainTableData}
page={tablePage}
+ // onRowClick={(ext) => {setSelectedGrainExtension(ext)}}
+ loadMore={() => {
+ setTableSize(tableSize + extraLoad)
+ }}
+ renderMobile={(row) =>
+ }>{row.name}
+
+ {supportedDisplay(row)}
+
+ }
pageSize={tableSize}
- columns={[
+ columns={[
{
title: "Name",
- render: row =>
+ render: row =>
{row.name}
@@ -162,7 +195,7 @@ export default function Grains() {
},
{
title: "Group",
- render: row =>
+ render: row =>
{row.group}
@@ -170,11 +203,27 @@ export default function Grains() {
},
{
title: "Equation",
- render: row =>
+ render: row =>
{equationName(row.equation ?? 0)}
+ },
+ {
+ title: "Kg Per Bushel",
+ render: row =>
+
+ {row.weightConversionKg.toFixed(2)}
+
+
+ },
+ {
+ title: "Bushels Per Tonne",
+ render: row =>
+
+ {row.bushelsPerTonne.toFixed(2)}
+
+
}
]}
handleRowsPerPageChange={(e) => {
@@ -189,9 +238,42 @@ export default function Grains() {
}
//the grain display for supported grain type to show the rest of the data such as what constants are used in the formula
- const supportedDisplay = () => {
+ const supportedDisplay = (grain?: GrainExtension) => {
return (
-
+
+ {grain ?
+
+
+ Info
+
+
+
+ Name: {grain.name}
+ Group: {grain.group}
+
+
+ Formula
+
+
+
+ Equation: {equationName(grain.equation)}
+ {/* Constant A: {grain.a}
+ Constant B: {grain.b}
+ Constant C: {grain.c} */}
+
+
+ Conversions
+
+
+
+ Kg per Bushel: {grain.weightConversionKg.toFixed(2)}
+ Bushels per Tonne: {grain.bushelsPerTonne.toFixed(2)}
+
+
+ :
+ Select a Grain from the table to view its details
+ }
+
)
}
@@ -201,6 +283,9 @@ export default function Grains() {
openSnack("Grain type updated")
}).catch(err => {
openSnack("There was an issue updating the selected grain type")
+ }).finally(() => {
+ setOpenUpdateConfirmation(false)
+ loadCustomGrain()
})
}
}
@@ -221,6 +306,19 @@ export default function Grains() {
)
}
+ //because the form is embedded on desktop this dialog is specifically for mobile view
+ const newGrainDialog = () => {
+ return (
+ setOpenNewGrainDialog(false)}>
+ Add New Grain
+
+ {customGrainDisplay()}
+
+
+
+ )
+ }
+
const addNewGrain = () => {
if(currentCustomGrain){
grainAPI.addGrain(currentCustomGrain).then(resp => {
@@ -228,6 +326,9 @@ export default function Grains() {
openSnack("New grain type added")
}).catch(err => {
openSnack("There was a problem creating the new grain type")
+ }).finally(() => {
+ loadCustomGrain()
+ setOpenNewGrainDialog(false)//only for the mobile view since the desktop has it embedded
})
}
}
@@ -238,6 +339,9 @@ export default function Grains() {
openSnack("Grain type removed")
}).catch(err => {
openSnack("There was a problem removing the selected grain type")
+ }).finally(() => {
+ setOpenRemoveConfirmation(false)
+ loadCustomGrain()
})
}
}
@@ -272,7 +376,8 @@ export default function Grains() {
{selectedGrain &&
}
-
+ {isMobile && !selectedGrain && }
+
)
@@ -283,7 +388,7 @@ export default function Grains() {
return (
{
setCurrentCustomGrain(newSettings)
setFormValid(formValid)
@@ -295,30 +400,41 @@ export default function Grains() {
}
const desktopView = () => {
- return (
+ if(displayCustom){
+ return (
- {displayCustom ? grainObjectTable() : supportedGrainTable()}
+ {grainObjectTable()}
- {displayCustom ? customGrainDisplay() : supportedDisplay()}
+ {customGrainDisplay()}
- )
+ )
+ }else{
+ return (
+
+ {supportedGrainTable()}
+
+ )
+ }
}
const mobileView = () => {
return (
-
+
+ {displayCustom ? grainObjectTable() : supportedGrainTable()}
+
)
}
return (
-
- GRAIN PAGE
+
+
+ Adaptive Grains
-
+
+ {isMobile && displayCustom &&
+ {
+ setSelectedGrain(undefined)
+ setCurrentCustomGrain(undefined)
+ setOpenNewGrainDialog(true)
+ }}>
+
+
+ }
{!isMobile ? desktopView() : mobileView()}
+
{updateGrainConfirmation()}
{removeConfirmation()}
+ {newGrainDialog()}
)
}
\ No newline at end of file