finished designing the list and mobile view of the field list
This commit is contained in:
parent
3567190c3a
commit
4db21a3d0c
9 changed files with 287 additions and 113 deletions
|
|
@ -1,7 +1,13 @@
|
|||
import {
|
||||
Accordion,
|
||||
AccordionDetails,
|
||||
AccordionSummary,
|
||||
Box,
|
||||
Button,
|
||||
Grid2,
|
||||
IconButton,
|
||||
Paper,
|
||||
Stack,
|
||||
Tab,
|
||||
Table,
|
||||
TableBody,
|
||||
|
|
@ -13,15 +19,20 @@ import {
|
|||
Typography
|
||||
} from "@mui/material";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { useMobile, useSnackbar } from "hooks";
|
||||
import { useGlobalState, useFieldAPI } from "providers";
|
||||
import { useMobile, useSnackbar, useUserAPI } from "hooks";
|
||||
import { useGlobalState, useFieldAPI, useJohnDeereProxyAPI, useCNHiProxyAPI } from "providers";
|
||||
//import HarvestTable from "harvestPlan/HarvestTable";
|
||||
import { Field } from "models";
|
||||
import { Field, fieldScope, teamScope } from "models";
|
||||
import GrainDescriber from "grain/GrainDescriber";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
import HarvestSettings from "harvestPlan/HarvestSettings";
|
||||
import ResponsiveTable, { Column } from "common/ResponsiveTable";
|
||||
import FieldMinimap from "./Fieldminimap";
|
||||
import FieldActions from "./FieldActions";
|
||||
import FieldSettings from "./FieldSettings";
|
||||
import { ExpandMore, Settings } from "@mui/icons-material";
|
||||
import { cloneDeep } from "lodash";
|
||||
import ShareAllFields from "./ShareAllFields";
|
||||
|
||||
interface TabPanelProps {
|
||||
children?: React.ReactNode;
|
||||
|
|
@ -29,61 +40,73 @@ interface TabPanelProps {
|
|||
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 jdAPI = useJohnDeereProxyAPI();
|
||||
const cnhAPI = useCNHiProxyAPI();
|
||||
const { openSnack } = useSnackbar();
|
||||
const [value, setValue] = React.useState(0);
|
||||
const [tabValue, setTabValue] = React.useState(0);
|
||||
const [openHarvestSettings, setOpenHarvestSettings] = useState(false);
|
||||
const [fieldForPlan, setFieldForPlan] = useState(Field.create());
|
||||
const [fields, setFields] = useState<Field[]>([]); //all fields
|
||||
const [adaptiveFields, setAdaptiveFields] = useState<Field[]>([])
|
||||
const [jdFields, setJDFields] = useState<Field[]>([])
|
||||
const [cnhFields, setCNHFields] = useState<Field[]>([])
|
||||
// const [fieldForPlan, setFieldForPlan] = useState(Field.create());
|
||||
const [fields, setFields] = useState<Field[]>([]);
|
||||
const [total, setTotal] = useState(0)
|
||||
const [rowsPerPage, setRowsPerPage] = useState(5)
|
||||
const [page, setPage] = useState(0)
|
||||
const [selectedField, setSelectedField] = useState<Field>()
|
||||
const [openFieldSettings, setOpenFieldSettings] = useState(false)
|
||||
const [shareOpen, setShareOpen] = useState(false)
|
||||
|
||||
const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => {
|
||||
setValue(newValue);
|
||||
setTabValue(newValue);
|
||||
setPage(0)
|
||||
};
|
||||
|
||||
const loadFields = useCallback(() => {
|
||||
const loadAdaptiveFields = useCallback(() => {
|
||||
fieldAPI
|
||||
.listFields(500, 0, "asc", "fieldName", undefined, as)
|
||||
.listFields(rowsPerPage, page*rowsPerPage, "asc", "fieldName", undefined, as)
|
||||
.then(resp => {
|
||||
setAdaptiveFields(resp.data.fields.map(f => Field.any(f)));
|
||||
// resp.data.fields.forEach(f => {
|
||||
// if (f.settings) {
|
||||
// fieldAPI.updateField(f.settings.key, f.settings);
|
||||
// }
|
||||
// });
|
||||
setFields(resp.data.fields.map(f => Field.create(f)));
|
||||
setTotal(resp.data.total)
|
||||
})
|
||||
.catch(err => {
|
||||
openSnack("Failed to load Field Mapping");
|
||||
openSnack("Failed to load Fields");
|
||||
});
|
||||
}, [fieldAPI, as, openSnack]);
|
||||
}, [fieldAPI, as, openSnack, page, rowsPerPage]);
|
||||
|
||||
const loadJDFields = useCallback(() => {
|
||||
jdAPI.listFields(rowsPerPage, page*rowsPerPage, as)
|
||||
.then(resp => {
|
||||
resp.data.fields ? setFields(resp.data.fields.map(f => Field.create(f))) : setFields([])
|
||||
}).catch(err => {
|
||||
openSnack("Failed to load John Deere Fields");
|
||||
})
|
||||
},[jdAPI, as, openSnack, page, rowsPerPage])
|
||||
|
||||
const loadCNHFields = useCallback(() => {
|
||||
cnhAPI.listFields(rowsPerPage, page*rowsPerPage, as)
|
||||
.then(resp => {
|
||||
resp.data.fields ? setFields(resp.data.fields.map(f => Field.create(f))) : setFields([])
|
||||
}).catch(err => {
|
||||
openSnack("Failed to load John Deere Fields");
|
||||
})
|
||||
},[cnhAPI, as, openSnack, page, rowsPerPage])
|
||||
|
||||
useEffect(() => {
|
||||
loadFields();
|
||||
}, [loadFields]);
|
||||
|
||||
useEffect(()=>{
|
||||
setFields(adaptiveFields.concat(jdFields, cnhFields))
|
||||
},[adaptiveFields,jdFields,cnhFields])
|
||||
//based on the current tab load the correct fields
|
||||
switch(tabValue){
|
||||
case 0:
|
||||
loadAdaptiveFields();
|
||||
break;
|
||||
case 1:
|
||||
loadJDFields();
|
||||
break;
|
||||
case 2:
|
||||
loadCNHFields();
|
||||
break;
|
||||
}
|
||||
}, [loadAdaptiveFields, loadJDFields, loadCNHFields, tabValue]);
|
||||
|
||||
const calcTotalAcres = (fields: Field[]) => {
|
||||
let totalAcres = 0;
|
||||
|
|
@ -94,6 +117,19 @@ export default function FieldList() {
|
|||
return totalAcres;
|
||||
};
|
||||
|
||||
const fieldActions = (field: Field) => {
|
||||
return (
|
||||
<Box>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
setSelectedField(field)
|
||||
setOpenFieldSettings(true)
|
||||
}}><Settings /></IconButton>
|
||||
<FieldActions field={field} permissions={field.permissions} refreshCallback={()=>{}}/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
const columns: Column<Field>[] = [
|
||||
{
|
||||
title: "View",
|
||||
|
|
@ -108,26 +144,25 @@ const columns: Column<Field>[] = [
|
|||
{
|
||||
title: "Field Name",
|
||||
render: row => {
|
||||
return <Typography>{row.name()}</Typography>
|
||||
return <Typography paddingLeft={2}>{row.name()}</Typography>
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Current Crop",
|
||||
render: row => {
|
||||
return <Typography>{row.crop()}</Typography>
|
||||
return <Typography paddingLeft={2}>{GrainDescriber(row.crop()).name}</Typography>
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Acres",
|
||||
render: row => {
|
||||
return <Typography>{row.acres()}</Typography>
|
||||
return <Typography paddingLeft={2}>{row.acres()}</Typography>
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Actions",
|
||||
render: row => {
|
||||
return <Typography>Field Actions Here</Typography>
|
||||
}
|
||||
hidden: tabValue > 0,
|
||||
render: row => fieldActions(row)
|
||||
}
|
||||
]
|
||||
|
||||
|
|
@ -136,43 +171,126 @@ const columns: Column<Field>[] = [
|
|||
<ResponsiveTable<Field>
|
||||
columns={columns}
|
||||
rows={fields}
|
||||
setPage={()=>{}}
|
||||
handleRowsPerPageChange={()=>{}}
|
||||
page={0}
|
||||
pageSize={1}
|
||||
total={fields.length}
|
||||
setPage={(newPage)=>{
|
||||
setPage(newPage)
|
||||
}}
|
||||
handleRowsPerPageChange={(e)=>{setRowsPerPage(e.target.value)}}
|
||||
page={page}
|
||||
pageSize={rowsPerPage}
|
||||
total={total}
|
||||
resizeable
|
||||
loadMore={() => {
|
||||
let currentFields = cloneDeep(fields)
|
||||
switch(tabValue){
|
||||
case 0:
|
||||
fieldAPI.listFields(rowsPerPage, currentFields.length, "asc", "fieldName", undefined, as).then(resp => {
|
||||
setFields(currentFields.concat(resp.data.fields.map(f => Field.create(f))))
|
||||
}).catch(err => {
|
||||
openSnack("Failed to load more fields")
|
||||
})
|
||||
break;
|
||||
case 1:
|
||||
jdAPI.listFields(rowsPerPage, currentFields.length, as).then(resp => {
|
||||
resp.data.fields ? setFields(currentFields.concat(resp.data.fields.map(f => Field.create(f)))) : setFields([])
|
||||
}).catch(err => {
|
||||
openSnack("Failed to load more fields")
|
||||
})
|
||||
break;
|
||||
case 2:
|
||||
cnhAPI.listFields(rowsPerPage, currentFields.length, as).then(resp => {
|
||||
resp.data.fields ? setFields(currentFields.concat(resp.data.fields.map(f => Field.create(f)))) : setFields([])
|
||||
}).catch(err => {
|
||||
openSnack("Failed to load more fields")
|
||||
})
|
||||
break;
|
||||
}
|
||||
}}
|
||||
renderMobile={(row, index) => {
|
||||
return (
|
||||
<Accordion>
|
||||
<AccordionSummary expandIcon={<ExpandMore />}>
|
||||
<Grid2 alignItems="center" width="100%" container direction={"row"} justifyContent="space-between" wrap="nowrap">
|
||||
<Grid2>
|
||||
<Typography textAlign="center">
|
||||
{row.name()}
|
||||
</Typography>
|
||||
</Grid2>
|
||||
<Grid2>
|
||||
{fieldActions(row)}
|
||||
</Grid2>
|
||||
</Grid2>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Grid2 alignItems="center" width="100%" container direction={"row"} justifyContent="space-between" wrap="nowrap">
|
||||
<Grid2 size={6}>
|
||||
<Box height={150} width="100%">
|
||||
<FieldMinimap field={row} />
|
||||
</Box>
|
||||
</Grid2>
|
||||
<Grid2 size={6}>
|
||||
<Stack spacing={2}>
|
||||
<Typography textAlign="center">{row.grainName()}</Typography>
|
||||
<Typography textAlign="center">{row.acres()} Acres</Typography>
|
||||
</Stack>
|
||||
</Grid2>
|
||||
</Grid2>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
{isMobile &&
|
||||
<Button
|
||||
onClick={()=>{setShareOpen(true)}}
|
||||
variant="contained"
|
||||
color="primary">
|
||||
Share All
|
||||
</Button>
|
||||
}
|
||||
<Tabs
|
||||
value={value}
|
||||
value={tabValue}
|
||||
onChange={handleChange}
|
||||
TabIndicatorProps={{ style: { background: "rgba(255,255,0,255)" } }}>
|
||||
<Tab label="All" />
|
||||
<Tab label="Adaptive" />
|
||||
<Tab label="John Deere" />
|
||||
<Tab label="Case New Holland" />
|
||||
{!isMobile &&
|
||||
<Button
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
right: 0
|
||||
}}
|
||||
onClick={()=>{setShareOpen(true)}}
|
||||
variant="contained"
|
||||
color="primary">
|
||||
Share All
|
||||
</Button>
|
||||
}
|
||||
</Tabs>
|
||||
<TabPanelMine index={0} value={value}>
|
||||
{fieldTable(fields)}
|
||||
</TabPanelMine>
|
||||
<TabPanelMine index={1} value={value}>
|
||||
{fieldTable(adaptiveFields)}
|
||||
</TabPanelMine>
|
||||
<TabPanelMine index={2} value={value}>
|
||||
{fieldTable(jdFields)}
|
||||
</TabPanelMine>
|
||||
<TabPanelMine index={3} value={value}>
|
||||
{fieldTable(cnhFields)}
|
||||
</TabPanelMine>
|
||||
<HarvestSettings
|
||||
{fieldTable(fields)}
|
||||
{/* <HarvestSettings
|
||||
open={openHarvestSettings}
|
||||
close={() => setOpenHarvestSettings(false)}
|
||||
field={fieldForPlan}
|
||||
/> */}
|
||||
<FieldSettings
|
||||
selectedField={selectedField}
|
||||
removeField={() => {
|
||||
//will need to re-load the fields after one is deleted
|
||||
loadAdaptiveFields()
|
||||
}}
|
||||
open={openFieldSettings}
|
||||
onClose={() => {
|
||||
setOpenFieldSettings(false);
|
||||
}}
|
||||
/>
|
||||
<ShareAllFields open={shareOpen} close={() => {setShareOpen(false)}}/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
|
@ -40,16 +40,17 @@ export default function FieldMinimap(props: Props) {
|
|||
return geoCollection ?
|
||||
(
|
||||
<Map
|
||||
initialViewState={{
|
||||
bounds: field.fieldBounds(.001)
|
||||
}}
|
||||
zoom={1}
|
||||
maxBounds={
|
||||
field.fieldBounds(.001)
|
||||
}
|
||||
reuseMaps
|
||||
style={{width: "100%", height: "100%"}}
|
||||
mapboxAccessToken={MAPBOX_TOKEN}
|
||||
mapStyle="mapbox://styles/mapbox/satellite-streets-v11"
|
||||
>
|
||||
<GeoMapLayer
|
||||
objectCollection={geoCollection}
|
||||
setInteractiveLayers={()=>{}}
|
||||
/>
|
||||
</Map>
|
||||
)
|
||||
|
|
|
|||
43
src/field/ShareAllFields.tsx
Normal file
43
src/field/ShareAllFields.tsx
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import { Dialog, DialogActions, DialogContent, DialogTitle } from "@mui/material";
|
||||
import ResponsiveDialog from "common/ResponsiveDialog";
|
||||
|
||||
interface Props {
|
||||
open: boolean
|
||||
close: () => void
|
||||
}
|
||||
|
||||
export default function ShareAllFields(props: Props) {
|
||||
const {open, close} = props
|
||||
|
||||
const share = () => {
|
||||
//if sharing to team use shareAllByKey
|
||||
|
||||
//if sharing to user use shareAll
|
||||
}
|
||||
|
||||
const target = () => {
|
||||
// toggle for whether sharing to user or team
|
||||
// if sharing to user have a text field
|
||||
// if sharing to team have the team selector
|
||||
}
|
||||
|
||||
const permissions = () => {
|
||||
// the checkboxes of permissions
|
||||
}
|
||||
|
||||
const actions = () => {
|
||||
//the buttons to shaare or cancel
|
||||
}
|
||||
|
||||
return (
|
||||
<ResponsiveDialog open={open} onClose={close}>
|
||||
<DialogTitle>Share All Fields</DialogTitle>
|
||||
<DialogContent>
|
||||
Content for selecting target and permissions
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
Buttons for cancel and confirm
|
||||
</DialogActions>
|
||||
</ResponsiveDialog>
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue