314 lines
No EOL
9.2 KiB
TypeScript
314 lines
No EOL
9.2 KiB
TypeScript
import { Field, fieldScope, HarvestPlan, teamScope } from "models";
|
|
import { useFieldAPI, useGlobalState, useHarvestPlanAPI } from "providers";
|
|
import React, { useCallback, useEffect, useState } from "react";
|
|
import { useLocation, useNavigate, useParams } from "react-router-dom";
|
|
import PageContainer from "./PageContainer";
|
|
import { useMobile, useSnackbar, useUserAPI } from "hooks";
|
|
import { Box, Card, Grid2, IconButton, Tab, Tabs, Theme, Typography, darken } from "@mui/material";
|
|
import FieldActions from "field/FieldActions";
|
|
import { Settings } from "@mui/icons-material";
|
|
import { pond } from "protobuf-ts/pond";
|
|
import FieldMinimap from "field/Fieldminimap";
|
|
import Weather from "weather/weather";
|
|
import TaskViewer from "tasks/TaskViewer";
|
|
import HarvestPlanDisplay from "harvestPlan/HarvestPlanDisplay";
|
|
import { cloneDeep } from "lodash";
|
|
import HarvestPlanTable from "harvestPlan/HarvestPlanTable";
|
|
import { makeStyles } from "@mui/styles";
|
|
import FieldSettings from "field/FieldSettings";
|
|
|
|
const useStyles = makeStyles((theme: Theme) => {
|
|
return ({
|
|
card: {
|
|
height: "100%",
|
|
bgcolor: darken(theme.palette.background.paper, 0.05),
|
|
},
|
|
sectionHeader: {
|
|
fontSize: 35,
|
|
fontWeight: 650
|
|
}
|
|
})
|
|
})
|
|
|
|
interface TabPanelProps {
|
|
children?: React.ReactNode;
|
|
index: any;
|
|
value: any;
|
|
}
|
|
|
|
function TabPanel(props: TabPanelProps) {
|
|
const { children, value, index, ...other } = props;
|
|
|
|
return (
|
|
<div
|
|
role="tabpanel"
|
|
hidden={value !== index}
|
|
aria-labelledby={`simple-tab-${index}`}
|
|
style={{ height: "94%", paddingTop: "15px" }}
|
|
{...other}>
|
|
{value === index && <React.Fragment>{children}</React.Fragment>}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default function FieldPage() {
|
|
const { state } = useLocation();
|
|
//const [{ as }] = useGlobalState()
|
|
const classes = useStyles();
|
|
const fieldAPI = useFieldAPI();
|
|
const userAPI = useUserAPI();
|
|
const [{as, user}] = useGlobalState();
|
|
const {openSnack} = useSnackbar();
|
|
const fieldID = useParams<{ fieldID: string }>()?.fieldID ?? "";
|
|
const [field, setField] = useState<Field>(state?.field ? Field.create(state.field) : Field.create())
|
|
const [permissions, setPermissions] = useState<pond.Permission[]>(state?.permissions ? state.permissions : [])
|
|
const isMobile = useMobile()
|
|
const [openFieldSettings, setOpenFieldSettings] = useState(false)
|
|
const [planLoading, setPlanLoading] = useState(false)
|
|
const [hPlan, setHPlan] = useState<HarvestPlan>(HarvestPlan.create());
|
|
const [loading, setLoading] = useState(false)
|
|
const hPlanAPI = useHarvestPlanAPI();
|
|
const [tabVal, setTabVal] = useState(0)
|
|
const navigate = useNavigate()
|
|
|
|
const loadField = useCallback(() => {
|
|
if(state.field){
|
|
fieldAPI.getField(state.field).then(resp => {
|
|
setField(Field.create(resp.data))
|
|
}).catch(err => {
|
|
|
|
})
|
|
}
|
|
},[as])
|
|
|
|
const goTo = () => {
|
|
navigate("/fields");
|
|
}
|
|
|
|
useEffect(()=>{
|
|
// if already loading do nothing
|
|
if(loading) return
|
|
//if the field is passed through in the state just use it
|
|
if(state?.field){
|
|
let field = Field.create(state.field)
|
|
field.permissions = state.permissions
|
|
setField(field)
|
|
setPermissions(state.permissions)
|
|
return
|
|
}
|
|
//otherwise load the field
|
|
setLoading(true)
|
|
fieldAPI.getField(fieldID, as).then(resp => {
|
|
setField(Field.create(resp.data))
|
|
|
|
}).catch(err => {
|
|
openSnack("Failed to load Field")
|
|
}).finally(() => {
|
|
setLoading(false)
|
|
})
|
|
//also get the permissions
|
|
let scope = fieldScope(fieldID);
|
|
if (as) {
|
|
scope = teamScope(as)
|
|
}
|
|
userAPI.getUser(user.id(), scope).then(resp => {
|
|
let f = cloneDeep(field)
|
|
f.permissions = resp.permissions
|
|
setField(f)
|
|
setPermissions(resp.permissions);
|
|
});
|
|
},[loadField, fieldID, as])
|
|
|
|
useEffect(() => {
|
|
if (field.key() !== "") {
|
|
hPlanAPI
|
|
.listHarvestPlans(1, 0, "desc", "createDate", field.key(), as)
|
|
.then(resp => {
|
|
if (resp.data.harvestPlan.length > 0) {
|
|
let plan = resp.data.harvestPlan[0];
|
|
setHPlan(HarvestPlan.any(plan));
|
|
} else {
|
|
setHPlan(HarvestPlan.create());
|
|
}
|
|
setPlanLoading(false);
|
|
})
|
|
.catch(err => {
|
|
//openSnack("Failed to load plan");
|
|
});
|
|
}
|
|
}, [field, as, hPlanAPI]);
|
|
|
|
const fieldActions = () => {
|
|
return (
|
|
<Grid2 container direction={"row"} justifyContent="space-between" alignContent="center" alignItems="center">
|
|
<Grid2>
|
|
<Typography className={classes.sectionHeader}>
|
|
{field.name()}
|
|
</Typography>
|
|
</Grid2>
|
|
<Grid2>
|
|
<IconButton
|
|
onClick={() => {
|
|
setOpenFieldSettings(true)
|
|
}}><Settings /></IconButton>
|
|
<FieldActions field={field} permissions={permissions} refreshCallback={()=>{}}/>
|
|
</Grid2>
|
|
</Grid2>
|
|
)
|
|
}
|
|
|
|
const map = () => {
|
|
return (
|
|
<Box minHeight={550} height="100%">
|
|
<FieldMinimap field={field} borderSpacing={0.001} squared/>
|
|
</Box>
|
|
)
|
|
}
|
|
|
|
const weather = () => {
|
|
return (<Weather longitude={field.center().longitude} latitude={field.center().latitude} />)
|
|
}
|
|
|
|
const harvestPlans = () => {
|
|
return (
|
|
<HarvestPlanDisplay
|
|
plan={hPlan}
|
|
permissions={permissions}
|
|
planField={field}
|
|
loading={planLoading}
|
|
changePlan={updatedPlan => {
|
|
if (updatedPlan) {
|
|
setHPlan(updatedPlan);
|
|
}
|
|
}}
|
|
/>)
|
|
}
|
|
|
|
const tasks = () => {
|
|
let taskLoadKeys: string[] = [];
|
|
if (!planLoading) {
|
|
field.key() !== "" && taskLoadKeys.push(field.key());
|
|
//hPlan.key() !== "" && taskLoadKeys.push(hPlan.key());
|
|
}
|
|
return (<TaskViewer drawerView objectKey={field.key()} loadKeys={taskLoadKeys} overlayButton={isMobile}/>)
|
|
}
|
|
|
|
const desktopView = () => {
|
|
return (
|
|
<React.Fragment>
|
|
<Box>
|
|
<Grid2 container spacing={1}>
|
|
<Grid2 size={4}>
|
|
<Card raised className={classes.card}>
|
|
{map()}
|
|
</Card>
|
|
</Grid2>
|
|
<Grid2 size={3}>
|
|
<Card raised className={classes.card}>
|
|
{weather()}
|
|
</Card>
|
|
</Grid2>
|
|
<Grid2 size={5}>
|
|
<Card raised className={classes.card}>
|
|
{tasks()}
|
|
</Card>
|
|
</Grid2>
|
|
</Grid2>
|
|
</Box>
|
|
<Box>
|
|
<Typography className={classes.sectionHeader}>
|
|
Harvest Overview
|
|
</Typography>
|
|
<Grid2 container spacing={1}>
|
|
<Grid2 size={4}>
|
|
<Card raised className={classes.card}>
|
|
{harvestPlans()}
|
|
</Card>
|
|
</Grid2>
|
|
<Grid2 size={8}>
|
|
<Card raised>
|
|
<HarvestPlanTable field={field} planSelected={(plan) => {setHPlan(plan)}}/>
|
|
</Card>
|
|
</Grid2>
|
|
</Grid2>
|
|
</Box>
|
|
</React.Fragment>
|
|
)
|
|
}
|
|
|
|
const mobileView = () => {
|
|
return (
|
|
<React.Fragment>
|
|
<Tabs
|
|
value={tabVal}
|
|
onChange={(_, val) => {
|
|
setTabVal(val)
|
|
}}
|
|
TabIndicatorProps={{ style: { background: "rgba(255,255,0,255)" } }}>
|
|
<Tab label="Overview" />
|
|
<Tab label="Activities" />
|
|
<Tab label="Harvest" />
|
|
</Tabs>
|
|
<TabPanel value={tabVal} index={0}>
|
|
<Grid2 container spacing={2} direction="column">
|
|
<Grid2 size={12}>
|
|
<Card raised sx={{height: "50vh"}}>
|
|
{map()}
|
|
</Card>
|
|
</Grid2>
|
|
<Grid2 size={12}>
|
|
<Card raised>
|
|
{weather()}
|
|
</Card>
|
|
</Grid2>
|
|
</Grid2>
|
|
</TabPanel>
|
|
<TabPanel value={tabVal} index={1}>
|
|
<Card raised>
|
|
{tasks()}
|
|
</Card>
|
|
</TabPanel>
|
|
<TabPanel value={tabVal} index={2}>
|
|
<Grid2>
|
|
<Grid2 size={12}>
|
|
<Card raised className={classes.card}>
|
|
{harvestPlans()}
|
|
</Card>
|
|
</Grid2>
|
|
<Grid2>
|
|
<HarvestPlanTable field={field} planSelected={(plan) => {setHPlan(plan)}}/>
|
|
</Grid2>
|
|
</Grid2>
|
|
</TabPanel>
|
|
</React.Fragment>
|
|
)
|
|
}
|
|
|
|
const fieldSettings = () => {
|
|
return (
|
|
<FieldSettings
|
|
open={openFieldSettings}
|
|
onClose={() => {
|
|
setOpenFieldSettings(false)
|
|
}}
|
|
selectedField={field}
|
|
updateFields={() => {
|
|
loadField()
|
|
}}
|
|
removeField={()=>{
|
|
goTo()
|
|
}}
|
|
/>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<PageContainer>
|
|
<Box padding={2}>
|
|
{fieldActions()}
|
|
{isMobile ? mobileView() : desktopView()}
|
|
</Box>
|
|
{fieldSettings()}
|
|
</PageContainer>
|
|
)
|
|
} |