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
2
package-lock.json
generated
2
package-lock.json
generated
|
|
@ -10911,7 +10911,7 @@
|
||||||
},
|
},
|
||||||
"node_modules/protobuf-ts": {
|
"node_modules/protobuf-ts": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#1b6076608722fff5290457b077604bb0b38d5499",
|
"resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#70351a87aa4a83bd6274adf8e3b7937090ab4cc2",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"protobufjs": "^6.8.8"
|
"protobufjs": "^6.8.8"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -285,7 +285,6 @@ export default function ComponentCard(props: Props) {
|
||||||
// ) : (
|
// ) : (
|
||||||
<UnitMeasurementSummary
|
<UnitMeasurementSummary
|
||||||
component={component}
|
component={component}
|
||||||
excludedNodes={component.settings.excludedNodes}
|
|
||||||
reading={UnitMeasurement.convertLastMeasurement(measurements)}
|
reading={UnitMeasurement.convertLastMeasurement(measurements)}
|
||||||
//dense
|
//dense
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,13 @@
|
||||||
import {
|
import {
|
||||||
|
Accordion,
|
||||||
|
AccordionDetails,
|
||||||
|
AccordionSummary,
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
|
Grid2,
|
||||||
|
IconButton,
|
||||||
Paper,
|
Paper,
|
||||||
|
Stack,
|
||||||
Tab,
|
Tab,
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
|
|
@ -13,15 +19,20 @@ import {
|
||||||
Typography
|
Typography
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import React, { useCallback, useEffect, useState } from "react";
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
import { useMobile, useSnackbar } from "hooks";
|
import { useMobile, useSnackbar, useUserAPI } from "hooks";
|
||||||
import { useGlobalState, useFieldAPI } from "providers";
|
import { useGlobalState, useFieldAPI, useJohnDeereProxyAPI, useCNHiProxyAPI } from "providers";
|
||||||
//import HarvestTable from "harvestPlan/HarvestTable";
|
//import HarvestTable from "harvestPlan/HarvestTable";
|
||||||
import { Field } from "models";
|
import { Field, fieldScope, teamScope } from "models";
|
||||||
import GrainDescriber from "grain/GrainDescriber";
|
import GrainDescriber from "grain/GrainDescriber";
|
||||||
import { pond } from "protobuf-ts/pond";
|
import { pond } from "protobuf-ts/pond";
|
||||||
import HarvestSettings from "harvestPlan/HarvestSettings";
|
import HarvestSettings from "harvestPlan/HarvestSettings";
|
||||||
import ResponsiveTable, { Column } from "common/ResponsiveTable";
|
import ResponsiveTable, { Column } from "common/ResponsiveTable";
|
||||||
import FieldMinimap from "./Fieldminimap";
|
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 {
|
interface TabPanelProps {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
|
|
@ -29,61 +40,73 @@ interface TabPanelProps {
|
||||||
value: 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() {
|
export default function FieldList() {
|
||||||
const [{ as }] = useGlobalState();
|
const [{ as }] = useGlobalState();
|
||||||
const isMobile = useMobile();
|
const isMobile = useMobile();
|
||||||
const fieldAPI = useFieldAPI();
|
const fieldAPI = useFieldAPI();
|
||||||
|
const jdAPI = useJohnDeereProxyAPI();
|
||||||
|
const cnhAPI = useCNHiProxyAPI();
|
||||||
const { openSnack } = useSnackbar();
|
const { openSnack } = useSnackbar();
|
||||||
const [value, setValue] = React.useState(0);
|
const [tabValue, setTabValue] = React.useState(0);
|
||||||
const [openHarvestSettings, setOpenHarvestSettings] = useState(false);
|
const [openHarvestSettings, setOpenHarvestSettings] = useState(false);
|
||||||
const [fieldForPlan, setFieldForPlan] = useState(Field.create());
|
// const [fieldForPlan, setFieldForPlan] = useState(Field.create());
|
||||||
const [fields, setFields] = useState<Field[]>([]); //all fields
|
const [fields, setFields] = useState<Field[]>([]);
|
||||||
const [adaptiveFields, setAdaptiveFields] = useState<Field[]>([])
|
const [total, setTotal] = useState(0)
|
||||||
const [jdFields, setJDFields] = useState<Field[]>([])
|
const [rowsPerPage, setRowsPerPage] = useState(5)
|
||||||
const [cnhFields, setCNHFields] = useState<Field[]>([])
|
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) => {
|
const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => {
|
||||||
setValue(newValue);
|
setTabValue(newValue);
|
||||||
|
setPage(0)
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadFields = useCallback(() => {
|
const loadAdaptiveFields = useCallback(() => {
|
||||||
fieldAPI
|
fieldAPI
|
||||||
.listFields(500, 0, "asc", "fieldName", undefined, as)
|
.listFields(rowsPerPage, page*rowsPerPage, "asc", "fieldName", undefined, as)
|
||||||
.then(resp => {
|
.then(resp => {
|
||||||
setAdaptiveFields(resp.data.fields.map(f => Field.any(f)));
|
setFields(resp.data.fields.map(f => Field.create(f)));
|
||||||
// resp.data.fields.forEach(f => {
|
setTotal(resp.data.total)
|
||||||
// if (f.settings) {
|
|
||||||
// fieldAPI.updateField(f.settings.key, f.settings);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.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(() => {
|
useEffect(() => {
|
||||||
loadFields();
|
//based on the current tab load the correct fields
|
||||||
}, [loadFields]);
|
switch(tabValue){
|
||||||
|
case 0:
|
||||||
useEffect(()=>{
|
loadAdaptiveFields();
|
||||||
setFields(adaptiveFields.concat(jdFields, cnhFields))
|
break;
|
||||||
},[adaptiveFields,jdFields,cnhFields])
|
case 1:
|
||||||
|
loadJDFields();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
loadCNHFields();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}, [loadAdaptiveFields, loadJDFields, loadCNHFields, tabValue]);
|
||||||
|
|
||||||
const calcTotalAcres = (fields: Field[]) => {
|
const calcTotalAcres = (fields: Field[]) => {
|
||||||
let totalAcres = 0;
|
let totalAcres = 0;
|
||||||
|
|
@ -94,6 +117,19 @@ export default function FieldList() {
|
||||||
return totalAcres;
|
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>[] = [
|
const columns: Column<Field>[] = [
|
||||||
{
|
{
|
||||||
title: "View",
|
title: "View",
|
||||||
|
|
@ -108,26 +144,25 @@ const columns: Column<Field>[] = [
|
||||||
{
|
{
|
||||||
title: "Field Name",
|
title: "Field Name",
|
||||||
render: row => {
|
render: row => {
|
||||||
return <Typography>{row.name()}</Typography>
|
return <Typography paddingLeft={2}>{row.name()}</Typography>
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Current Crop",
|
title: "Current Crop",
|
||||||
render: row => {
|
render: row => {
|
||||||
return <Typography>{row.crop()}</Typography>
|
return <Typography paddingLeft={2}>{GrainDescriber(row.crop()).name}</Typography>
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Acres",
|
title: "Acres",
|
||||||
render: row => {
|
render: row => {
|
||||||
return <Typography>{row.acres()}</Typography>
|
return <Typography paddingLeft={2}>{row.acres()}</Typography>
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Actions",
|
title: "Actions",
|
||||||
render: row => {
|
hidden: tabValue > 0,
|
||||||
return <Typography>Field Actions Here</Typography>
|
render: row => fieldActions(row)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -136,43 +171,126 @@ const columns: Column<Field>[] = [
|
||||||
<ResponsiveTable<Field>
|
<ResponsiveTable<Field>
|
||||||
columns={columns}
|
columns={columns}
|
||||||
rows={fields}
|
rows={fields}
|
||||||
setPage={()=>{}}
|
setPage={(newPage)=>{
|
||||||
handleRowsPerPageChange={()=>{}}
|
setPage(newPage)
|
||||||
page={0}
|
}}
|
||||||
pageSize={1}
|
handleRowsPerPageChange={(e)=>{setRowsPerPage(e.target.value)}}
|
||||||
total={fields.length}
|
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 (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
|
{isMobile &&
|
||||||
|
<Button
|
||||||
|
onClick={()=>{setShareOpen(true)}}
|
||||||
|
variant="contained"
|
||||||
|
color="primary">
|
||||||
|
Share All
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
<Tabs
|
<Tabs
|
||||||
value={value}
|
value={tabValue}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
TabIndicatorProps={{ style: { background: "rgba(255,255,0,255)" } }}>
|
TabIndicatorProps={{ style: { background: "rgba(255,255,0,255)" } }}>
|
||||||
<Tab label="All" />
|
|
||||||
<Tab label="Adaptive" />
|
<Tab label="Adaptive" />
|
||||||
<Tab label="John Deere" />
|
<Tab label="John Deere" />
|
||||||
<Tab label="Case New Holland" />
|
<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>
|
</Tabs>
|
||||||
<TabPanelMine index={0} value={value}>
|
{fieldTable(fields)}
|
||||||
{fieldTable(fields)}
|
{/* <HarvestSettings
|
||||||
</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
|
|
||||||
open={openHarvestSettings}
|
open={openHarvestSettings}
|
||||||
close={() => setOpenHarvestSettings(false)}
|
close={() => setOpenHarvestSettings(false)}
|
||||||
field={fieldForPlan}
|
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>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -40,16 +40,17 @@ export default function FieldMinimap(props: Props) {
|
||||||
return geoCollection ?
|
return geoCollection ?
|
||||||
(
|
(
|
||||||
<Map
|
<Map
|
||||||
initialViewState={{
|
zoom={1}
|
||||||
bounds: field.fieldBounds(.001)
|
maxBounds={
|
||||||
}}
|
field.fieldBounds(.001)
|
||||||
|
}
|
||||||
|
reuseMaps
|
||||||
style={{width: "100%", height: "100%"}}
|
style={{width: "100%", height: "100%"}}
|
||||||
mapboxAccessToken={MAPBOX_TOKEN}
|
mapboxAccessToken={MAPBOX_TOKEN}
|
||||||
mapStyle="mapbox://styles/mapbox/satellite-streets-v11"
|
mapStyle="mapbox://styles/mapbox/satellite-streets-v11"
|
||||||
>
|
>
|
||||||
<GeoMapLayer
|
<GeoMapLayer
|
||||||
objectCollection={geoCollection}
|
objectCollection={geoCollection}
|
||||||
setInteractiveLayers={()=>{}}
|
|
||||||
/>
|
/>
|
||||||
</Map>
|
</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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -6,7 +6,7 @@ interface Props {
|
||||||
objectCollection: FeatureCollection;
|
objectCollection: FeatureCollection;
|
||||||
measurementCollection?: FeatureCollection;
|
measurementCollection?: FeatureCollection;
|
||||||
showTitle?: boolean;
|
showTitle?: boolean;
|
||||||
setInteractiveLayers: (layers: string[]) => void;
|
setInteractiveLayers?: (layers: string[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function GeoMapLayer(props: Props) {
|
export default function GeoMapLayer(props: Props) {
|
||||||
|
|
@ -18,7 +18,9 @@ export default function GeoMapLayer(props: Props) {
|
||||||
// }, [measurementCollection, objectCollection]);
|
// }, [measurementCollection, objectCollection]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setInteractiveLayers(["shapefill", "shapeborder", "measurementBorder"]);
|
if(setInteractiveLayers){
|
||||||
|
setInteractiveLayers(["shapefill", "shapeborder", "measurementBorder"]);
|
||||||
|
}
|
||||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -60,6 +62,7 @@ export default function GeoMapLayer(props: Props) {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Source>
|
</Source>
|
||||||
|
{measurementCollection &&
|
||||||
<Source type="geojson" data={measurementCollection} id={"measurements"}>
|
<Source type="geojson" data={measurementCollection} id={"measurements"}>
|
||||||
<Layer
|
<Layer
|
||||||
id="measurementBorder"
|
id="measurementBorder"
|
||||||
|
|
@ -90,6 +93,7 @@ export default function GeoMapLayer(props: Props) {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Source>
|
</Source>
|
||||||
|
}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,14 +10,16 @@ import { LngLat, LngLatBounds } from "mapbox-gl";
|
||||||
export class Field {
|
export class Field {
|
||||||
public settings: pond.FieldSettings = pond.FieldSettings.create();
|
public settings: pond.FieldSettings = pond.FieldSettings.create();
|
||||||
public status: pond.FieldStatus = pond.FieldStatus.create();
|
public status: pond.FieldStatus = pond.FieldStatus.create();
|
||||||
public permissions: pond.Permission[] = [];
|
public permissions: pond.Permission[] = [];
|
||||||
|
|
||||||
public static create(pf?: pond.Field): Field {
|
public static create(pf?: pond.Field): Field {
|
||||||
let my = new Field();
|
let my = new Field();
|
||||||
if (pf) {
|
if (pf) {
|
||||||
my.settings = pond.FieldSettings.fromObject(cloneDeep(or(pf.settings, {})));
|
//the from object is required here to deal with a protobuf quirk where enums would be seen as strings at runtime, but the code sees them as the actual value
|
||||||
my.status = pond.FieldStatus.fromObject(cloneDeep(or(pf.status, {})));
|
let field = pond.Field.fromObject(cloneDeep(or(pf, pond.Field.create())))
|
||||||
my.permissions = pf.fieldPermissions;
|
my.settings = field.settings ? field.settings : pond.FieldSettings.create()
|
||||||
|
my.status = field.status ? field.status : pond.FieldStatus.create()
|
||||||
|
my.permissions = field.fieldPermissions
|
||||||
}
|
}
|
||||||
return my;
|
return my;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { useGlobalState } from "providers";
|
||||||
import React, { createContext, PropsWithChildren, useContext } from "react";
|
import React, { createContext, PropsWithChildren, useContext } from "react";
|
||||||
import { or } from "utils";
|
import { or } from "utils";
|
||||||
import { pondURL } from "./pond";
|
import { pondURL } from "./pond";
|
||||||
|
import { permissionToString } from "pbHelpers/Permission";
|
||||||
|
|
||||||
export interface IFieldAPIContext {
|
export interface IFieldAPIContext {
|
||||||
addField: (field: pond.FieldSettings, otherTeam?: string) => Promise<any>;
|
addField: (field: pond.FieldSettings, otherTeam?: string) => Promise<any>;
|
||||||
|
|
@ -18,14 +19,6 @@ export interface IFieldAPIContext {
|
||||||
otherTeam?: string,
|
otherTeam?: string,
|
||||||
asRoot?: boolean
|
asRoot?: boolean
|
||||||
) => Promise<AxiosResponse<pond.ListFieldsResponse>>;
|
) => Promise<AxiosResponse<pond.ListFieldsResponse>>;
|
||||||
fieldsPageData: (
|
|
||||||
limit: number,
|
|
||||||
offset: number,
|
|
||||||
order?: "asc" | "desc",
|
|
||||||
orderBy?: string,
|
|
||||||
search?: string,
|
|
||||||
otherTeam?: string,
|
|
||||||
) => Promise<AxiosResponse<pond.ListFieldsPageDataResponse>>
|
|
||||||
removeField: (key: string, otherTeam?: string) => Promise<AxiosResponse<pond.RemoveFieldResponse>>;
|
removeField: (key: string, otherTeam?: string) => Promise<AxiosResponse<pond.RemoveFieldResponse>>;
|
||||||
updateField: (
|
updateField: (
|
||||||
key: string,
|
key: string,
|
||||||
|
|
@ -33,6 +26,14 @@ export interface IFieldAPIContext {
|
||||||
asRoot?: true,
|
asRoot?: true,
|
||||||
otherTeam?: string
|
otherTeam?: string
|
||||||
) => Promise<AxiosResponse<pond.UpdateFieldResponse>>;
|
) => Promise<AxiosResponse<pond.UpdateFieldResponse>>;
|
||||||
|
shareAll: (
|
||||||
|
email: string,
|
||||||
|
permissions: pond.Permission[],
|
||||||
|
) => Promise<AxiosResponse<any>>
|
||||||
|
shareAllByKey: (
|
||||||
|
key: string,
|
||||||
|
permissions: pond.Permission[]
|
||||||
|
) => Promise<AxiosResponse<any>>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FieldAPIContext = createContext<IFieldAPIContext>({} as IFieldAPIContext);
|
export const FieldAPIContext = createContext<IFieldAPIContext>({} as IFieldAPIContext);
|
||||||
|
|
@ -88,30 +89,6 @@ export default function FieldProvider(props: PropsWithChildren<Props>) {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fieldsPageData = (
|
|
||||||
limit: number,
|
|
||||||
offset: number,
|
|
||||||
order?: "asc" | "desc",
|
|
||||||
orderBy?: string,
|
|
||||||
search?: string,
|
|
||||||
otherTeam?: string,
|
|
||||||
) => {
|
|
||||||
const view = otherTeam ? otherTeam : as
|
|
||||||
return get<pond.ListFieldsPageDataResponse>(
|
|
||||||
pondURL(
|
|
||||||
"/fieldsPage" +
|
|
||||||
"?limit=" +
|
|
||||||
limit +
|
|
||||||
"&offset=" +
|
|
||||||
offset +
|
|
||||||
("&order=" + or(order, "asc")) +
|
|
||||||
("&by=" + or(orderBy, "key")) +
|
|
||||||
(view ? "&as=" + view : "") +
|
|
||||||
(search ? "&search=" + search : "")
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateField = (key: string, field: pond.FieldSettings, asRoot?: boolean, otherTeam?: string) => {
|
const updateField = (key: string, field: pond.FieldSettings, asRoot?: boolean, otherTeam?: string) => {
|
||||||
const view = otherTeam ? otherTeam : as
|
const view = otherTeam ? otherTeam : as
|
||||||
if (view)
|
if (view)
|
||||||
|
|
@ -125,6 +102,36 @@ const view = otherTeam ? otherTeam : as
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const shareAll = (email: string, permissions: pond.Permission[]) => {
|
||||||
|
let url = pondURL("/fields/shareAll")
|
||||||
|
if (as) url = url + "?as=" + as
|
||||||
|
return new Promise<AxiosResponse>((resolve, reject) => {
|
||||||
|
post(url, {
|
||||||
|
email: email,
|
||||||
|
permissions: permissions.map(permission => permissionToString(permission))
|
||||||
|
}).then(resp => {
|
||||||
|
return resolve(resp)
|
||||||
|
}).catch(err => {
|
||||||
|
return reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const shareAllByKey = (key: string, permissions: pond.Permission[]) => {
|
||||||
|
let url = pondURL("/fields/shareAllByKey")
|
||||||
|
if (as) url = url + "?as=" + as
|
||||||
|
return new Promise<AxiosResponse>((resolve, reject) => {
|
||||||
|
post(url, {
|
||||||
|
key: key,
|
||||||
|
permissions: permissions.map(permission => permissionToString(permission))
|
||||||
|
}).then(resp => {
|
||||||
|
return resolve(resp)
|
||||||
|
}).catch(err => {
|
||||||
|
return reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FieldAPIContext.Provider
|
<FieldAPIContext.Provider
|
||||||
value={{
|
value={{
|
||||||
|
|
@ -133,7 +140,8 @@ const view = otherTeam ? otherTeam : as
|
||||||
listFields,
|
listFields,
|
||||||
removeField,
|
removeField,
|
||||||
updateField,
|
updateField,
|
||||||
fieldsPageData
|
shareAll,
|
||||||
|
shareAllByKey
|
||||||
}}>
|
}}>
|
||||||
{children}
|
{children}
|
||||||
</FieldAPIContext.Provider>
|
</FieldAPIContext.Provider>
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,6 @@ export default function PermissionProvider(props: PropsWithChildren<Props>) {
|
||||||
let url = pondURL("/" + scope.kind + "s/" + scope.key + "/shareByKey")
|
let url = pondURL("/" + scope.kind + "s/" + scope.key + "/shareByKey")
|
||||||
if (as) url = pondURL(`/${scope.kind}s/${scope.key}/shareByKey?as=${as}`)
|
if (as) url = pondURL(`/${scope.kind}s/${scope.key}/shareByKey?as=${as}`)
|
||||||
return new Promise<AxiosResponse>((resolve, reject) => {
|
return new Promise<AxiosResponse>((resolve, reject) => {
|
||||||
|
|
||||||
post(url, {
|
post(url, {
|
||||||
key: key,
|
key: key,
|
||||||
permissions: permissions.map(permission => permissionToString(permission))
|
permissions: permissions.map(permission => permissionToString(permission))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue