From 424b75afb8c9a38dc2eafb0d63b7c2c3ee9ff7f8 Mon Sep 17 00:00:00 2001 From: csawatzky Date: Wed, 23 Apr 2025 13:25:45 -0600 Subject: [PATCH] put the jd and cnh pages in, still need to test that the re-directs still function as expected with the new platform --- src/app/App.tsx | 7 + src/integrations/CNHi/CNHiAccess.tsx | 203 ++++++++ src/integrations/JohnDeere/JDAccess.tsx | 215 +++++++++ .../mapObjectControllers/AgMapController.tsx | 116 ++--- src/navigation/Router.tsx | 15 +- src/navigation/SideNavigator.tsx | 30 ++ src/pages/CNHi.tsx | 148 ++++++ src/pages/JohnDeere.tsx | 453 ++++++++++++++++++ src/pages/Users.tsx | 3 +- src/providers/index.ts | 4 +- src/providers/pond/cnhiProxyAPI.tsx | 124 +++++ src/providers/pond/johnDeereProxyAPI.tsx | 127 +++++ src/providers/pond/pond.tsx | 12 +- 13 files changed, 1393 insertions(+), 64 deletions(-) create mode 100644 src/integrations/CNHi/CNHiAccess.tsx create mode 100644 src/integrations/JohnDeere/JDAccess.tsx create mode 100644 src/pages/CNHi.tsx create mode 100644 src/pages/JohnDeere.tsx create mode 100644 src/providers/pond/cnhiProxyAPI.tsx create mode 100644 src/providers/pond/johnDeereProxyAPI.tsx diff --git a/src/app/App.tsx b/src/app/App.tsx index dbbcab1..e863c63 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -44,6 +44,13 @@ function App() { let client_id = import.meta.env.VITE_AUTH0_CLIENT_ID; // if (!client_id) client_id = import.meta.env.VITE_AUTH0_CLIENT_ID; + //check the url for a code before auth0 causes a login re-direct + if (window.location.pathname !== "/callback") { + //set the code into local storage + let code = new URLSearchParams(window.location.search).get("code"); + sessionStorage.setItem("code", code || "") + } + return ( ([]); + const [primaryUser, setPrimaryUser] = useState(""); + const [cnhiUsername, setCNHiUserName] = useState(""); + const [cnhiCode, setCNHiCode] = useState(""); + //const [activeStep, setActiveStep] = useState(0); + const userAPI = useUserAPI(); + const cnhiAPI = useCNHiProxyAPI(); + //const [dataOps, setDataOps] = useState([]); + const { openSnack } = useSnackbar(); + const search = useLocation().search; + const searchParams = new URLSearchParams(search); + //const [{ as }] = useGlobalState(); + const client_id = import.meta.env.VITE_CNHI_CLIENT_ID; + const auth_url = import.meta.env.VITE_CNHI_AUTHORIZE_URL; + const redirect_url = import.meta.env.VITE_CNHI_REDIRECT_URI; + const scopes = import.meta.env.VITE_CNHI_SCOPES; + const connection = import.meta.env.VITE_CNHI_CONNECTION; + const audience = import.meta.env.VITE_CNHI_AUDIENCE; + + const submitNewOrganization = () => { + if (cnhiCode) { + cnhiAPI + .addAccount(teamKey, primaryUser, cnhiCode, cnhiUsername) + .then(resp => { + //the code was used so remove it from local storage + if (localStorage.getItem("code")) { + localStorage.removeItem("code"); + } + openSnack("Added New Case New Holland Account Link"); + }) + .catch(err => { + openSnack("Failed to Add New Case New Holland Account Link"); + }); + setOpenDialog(false); + } + }; + + useEffect(() => { + if (teamKey !== "") { + userAPI.listObjectUsers(teamScope(teamKey)).then(resp => { + setTeamUsers(resp.data.users.map((u: pond.User) => User.any(u))); + }); + } + }, [teamKey, userAPI]); + + useEffect(() => { + let code = localStorage.getItem("code"); + if (code) { + setCNHiCode(code); + setOpenDialog(true); + } + }, [searchParams, cnhiCode]); + + const validate = () => { + let invalid = false; + if (cnhiUsername === "" || teamKey === "" || primaryUser === "" || cnhiCode === "") { + invalid = true; + } + return invalid; + }; + + const general = () => { + return ( + + setCNHiUserName(e.target.value)} + /> + + setPrimaryUser(e.target.value)}> + {teamUsers.map(u => ( + + + + + + + {u.name()} + + + + ))} + + setCNHiCode(e.target.value)} + /> + + ); + }; + + const newOrgDialog = () => { + return ( + { + setOpenDialog(false); + }}> + Enter New Integration Link + {general()} + + + + + + ); + }; + + return ( + + + {newOrgDialog()} + + {/* Admin testing buttons */} + {/* + + */} + + ); +} diff --git a/src/integrations/JohnDeere/JDAccess.tsx b/src/integrations/JohnDeere/JDAccess.tsx new file mode 100644 index 0000000..a38e9d8 --- /dev/null +++ b/src/integrations/JohnDeere/JDAccess.tsx @@ -0,0 +1,215 @@ +import { + Avatar, + Box, + Button, + DialogActions, + DialogContent, + DialogTitle, + Grid2 as Grid, + MenuItem, + TextField +} from "@mui/material"; +import ResponsiveDialog from "common/ResponsiveDialog"; +import { teamScope, User } from "models"; +import { pond } from "protobuf-ts/pond"; +import { useSnackbar, useUserAPI, useJohnDeereProxyAPI } from "providers"; +import { useEffect, useState } from "react"; +import { useLocation } from "react-router"; +import TeamSearch from "teams/TeamSearch"; + +//const steps = [{label: "General"},{label: "Data Options"}] + +export default function JDAccess() { + const [openDialog, setOpenDialog] = useState(false); + const [teamKey, setTeamKey] = useState(""); + const [teamUsers, setTeamUsers] = useState([]); + const [primaryUser, setPrimaryUser] = useState(""); + const [jdUserName, setJDUserName] = useState(""); + const [jdCode, setJDCode] = useState(""); + //const [activeStep, setActiveStep] = useState(0); + const userAPI = useUserAPI(); + const johnDeereAPI = useJohnDeereProxyAPI(); + //const [dataOps, setDataOps] = useState([]); + const { openSnack } = useSnackbar(); + const search = useLocation().search; + const searchParams = new URLSearchParams(search); + //const [{ as }] = useGlobalState(); + //const MAPBOX_TOKEN = import.meta.env.VITE_MAPBOX_ACCESS_TOKEN; + const client_id = import.meta.env.VITE_JD_CLIENT_ID; + const auth_url = import.meta.env.VITE_JD_AUTHORIZE_URL; + const redirect_url = import.meta.env.VITE_JD_REDIRECT_URI; + const scopes = import.meta.env.VITE_JD_SCOPES; + const state = import.meta.env.VITE_JD_STATE; + + + const submitNewOrganization = () => { + //api call to add the new organization + if (jdCode) { + johnDeereAPI + .addAccount(teamKey, primaryUser, jdCode, jdUserName) + .then(resp => { + //the code was used so remove it from local storage + if (sessionStorage.getItem("code")) { + sessionStorage.removeItem("code"); + } + openSnack("Added New John Deere Account Link"); + window.open( + `https://connections.deere.com/connections/${client_id}/select-organizations` + ); + }) + .catch(err => { + openSnack("Failed to Add New John Deere Account Link"); + }); + setOpenDialog(false); + } + }; + + useEffect(() => { + if (teamKey !== "") { + userAPI.listObjectUsers(teamScope(teamKey)).then(resp => { + setTeamUsers(resp.data.users.map((u: pond.User) => User.any(u))); + }); + } + }, [teamKey, userAPI]); + + useEffect(() => { + let code = sessionStorage.getItem("code"); + if (code) { + setJDCode(code); + setOpenDialog(true); + } + }, [searchParams, jdCode]); + + const validate = () => { + let invalid = false; + if (jdUserName === "" || teamKey === "" || primaryUser === "" || jdCode === "") { + invalid = true; + } + return invalid; + }; + + const general = () => { + return ( + + setJDUserName(e.target.value)} + /> + + setPrimaryUser(e.target.value)}> + {teamUsers.map(u => ( + + + + + + + {u.name()} + + + + ))} + + setJDCode(e.target.value)} + /> + + ); + }; + + const newOrgDialog = () => { + return ( + { + setOpenDialog(false); + }}> + Enter New Integration Link + {general()} + + + + + + ); + }; + + return ( + + + {newOrgDialog()} + {/* admin buttons for testing */} + {/* + */} + + ); +} diff --git a/src/maps/mapObjectControllers/AgMapController.tsx b/src/maps/mapObjectControllers/AgMapController.tsx index 497a842..4f95a10 100644 --- a/src/maps/mapObjectControllers/AgMapController.tsx +++ b/src/maps/mapObjectControllers/AgMapController.tsx @@ -25,7 +25,9 @@ import { useBinAPI, useFieldMarkerAPI, useDeviceAPI, - useGrainBagAPI + useGrainBagAPI, + useJohnDeereProxyAPI, + useCNHiProxyAPI } from "providers"; import { pond } from "protobuf-ts/pond"; import { Bin as IBin, Field, FieldMarker, Device as DeviceModel } from "models"; @@ -95,8 +97,8 @@ import { Result } from "@mapbox/mapbox-gl-geocoder"; const isMobile = useMobile(); const userId = user.id(); const fieldAPI = useFieldAPI(); - //const johnDeereAPI = useJohnDeereProxyAPI(); - //const cnhiAPI = useCNHiProxyAPI(); + const johnDeereAPI = useJohnDeereProxyAPI(); + const cnhiAPI = useCNHiProxyAPI(); const { openSnack } = useSnackbar(); const grainBagAPI = useGrainBagAPI(); const binYardAPI = useBinYardAPI(); @@ -246,61 +248,61 @@ import { Result } from "@mapbox/mapbox-gl-geocoder"; /** * used to load the fields managed by John Deere */ - // const loadJDFields = useCallback(() => { - // johnDeereAPI - // .listFields(100, 0, as) - // .then(resp => { - // let fields: Map = new Map(); - // let fieldEntries: GeocoderObject[] = []; - // if (resp.data.fields) { - // resp.data.fields.forEach(f => { - // let field = Field.any(f); - // fields.set(field.key(), field); - // fieldEntries.push({ - // id: field.key(), - // place_name: field.name(), - // place_type: ["field"], - // center: [field.center().longitude, field.center().latitude] - // }); - // }); - // } - // setJDFields(fields); - // setJDFieldSearchEntries(fieldEntries); - // }) - // .catch(err => { - // openSnack("Failed to load JD Field Mappings"); - // }); - // }, [johnDeereAPI, as, openSnack]); + const loadJDFields = useCallback(() => { + johnDeereAPI + .listFields(100, 0, as) + .then(resp => { + let fields: Map = new Map(); + let fieldEntries: Result[] = []; + if (resp.data.fields) { + resp.data.fields.forEach(f => { + let field = Field.any(f); + fields.set(field.key(), field); + fieldEntries.push({ + id: field.key(), + place_name: field.name(), + place_type: ["field"], + center: [field.center().longitude, field.center().latitude] + } as Result); + }); + } + setJDFields(fields); + setJDFieldSearchEntries(fieldEntries); + }) + .catch(err => { + openSnack("Failed to load JD Field Mappings"); + }); + }, [johnDeereAPI, as, openSnack]); /** * used to load the fields managed by John Deere */ - // const loadCNHIFields = useCallback(() => { - // cnhiAPI - // .listFields(100, 0, as) - // .then(resp => { - // let fields: Map = new Map(); - // let fieldEntries: GeocoderObject[] = []; - // if (resp.data.fields) { - // resp.data.fields.forEach(f => { - // let field = Field.any(f); - // fields.set(field.key(), field); - // fieldEntries.push({ - // id: field.key(), - // place_name: field.name(), - // place_type: ["field"], - // center: [field.center().longitude, field.center().latitude] - // }); - // }); - // } + const loadCNHIFields = useCallback(() => { + cnhiAPI + .listFields(100, 0, as) + .then(resp => { + let fields: Map = new Map(); + let fieldEntries: Result[] = []; + if (resp.data.fields) { + resp.data.fields.forEach(f => { + let field = Field.any(f); + fields.set(field.key(), field); + fieldEntries.push({ + id: field.key(), + place_name: field.name(), + place_type: ["field"], + center: [field.center().longitude, field.center().latitude] + } as Result); + }); + } - // setCNHIFields(fields); - // setCNHIFieldSearchEntries(fieldEntries); - // }) - // .catch(err => { - // openSnack("Failed to load CNHI Field Mappings"); - // }); - // }, [cnhiAPI, as, openSnack]); + setCNHIFields(fields); + setCNHIFieldSearchEntries(fieldEntries); + }) + .catch(err => { + openSnack("Failed to load CNHI Field Mappings"); + }); + }, [cnhiAPI, as, openSnack]); const loadGrainBags = useCallback(() => { grainBagAPI @@ -761,8 +763,8 @@ import { Result } from "@mapbox/mapbox-gl-geocoder"; if (!loading) { setLoading(true); loadFields(); - //loadJDFields(); - //loadCNHIFields(); + loadJDFields(); + loadCNHIFields(); loadBins(); loadYards(); loadDevices(); @@ -776,8 +778,8 @@ import { Result } from "@mapbox/mapbox-gl-geocoder"; loadDevices, loadFieldMarkers, loadFields, - //loadJDFields, - //loadCNHIFields, + loadJDFields, + loadCNHIFields, loadGrainBags, loading ]); diff --git a/src/navigation/Router.tsx b/src/navigation/Router.tsx index c52d5b8..ad07d56 100644 --- a/src/navigation/Router.tsx +++ b/src/navigation/Router.tsx @@ -7,7 +7,7 @@ import Logout from "pages/Logout"; import { ErrorBoundary } from "react-error-boundary"; import { getWhitelabel } from "services/whiteLabel"; import Ventilation from "pages/VentEditor"; -import { useGlobalState } from "providers"; +import { useGlobalState } from "providers"; const DeviceHistory = lazy(() => import("pages/DeviceHistory")); const DevicePage = lazy(() => import("pages/Device")); @@ -42,6 +42,8 @@ const Firmware = lazy(() => import("pages/Firmware")); const Nfc = lazy(() => import("pages/Nfc")); const Contracts = lazy(() => import("pages/Contracts")); const Contract = lazy(() => import("pages/Contract")); +const JohnDeere = lazy(() => import("pages/JohnDeere")); +const CNHi = lazy(() => import("pages/CNHi")); export const appendToUrl = (appendage: number | string) => { const basePath = location.pathname.replace(/\/$/, ""); @@ -321,8 +323,15 @@ export default function Router() { } /> } /> } /> - - {/* Map pages */} + + {/* integration routes */} + {user.hasFeature("john-deere") && + } /> + } + {user.hasFeature("cnhi") && + } /> + } + {/* Map routes */} } /> } /> } /> diff --git a/src/navigation/SideNavigator.tsx b/src/navigation/SideNavigator.tsx index 8124dea..25dfe9d 100644 --- a/src/navigation/SideNavigator.tsx +++ b/src/navigation/SideNavigator.tsx @@ -45,6 +45,8 @@ import FieldListIcon from "products/AgIcons/FieldList"; import MarketplaceIcon from "products/CommonIcons/marketplaceIcon"; import DataDuckIcon from "products/Bindapt/DataDuckIcon"; import ContractsIcon from "products/CommonIcons/contractIcon"; +import JohnDeereIcon from "products/CommonIcons/johnDeereIcon"; +import CNHiIcon from "products/CommonIcons/cnhiIcon"; const drawerWidth = 230; @@ -439,6 +441,34 @@ export default function SideNavigator(props: Props) { {open && } + {user.hasFeature("john-deere") && + + goTo("/johndeere")} + classes={getClasses("/johndeere")} + > + + + + {open && } + + + } + {user.hasFeature("cnhi") && + + goTo("/cnhi")} + classes={getClasses("/cnhi")} + > + + + + {open && } + + + } ) } diff --git a/src/pages/CNHi.tsx b/src/pages/CNHi.tsx new file mode 100644 index 0000000..fb4b91e --- /dev/null +++ b/src/pages/CNHi.tsx @@ -0,0 +1,148 @@ +import { + Accordion, + AccordionDetails, + AccordionSummary, + Box, + Button, + Checkbox, + FormControlLabel, + Grid2 as Grid, + MenuItem, + Select +} from "@mui/material"; +import { ExpandMore } from "@mui/icons-material"; +import CNHiAccess from "integrations/CNHi/CNHiAccess"; +import { pond } from "protobuf-ts/pond"; +import { useGlobalState, useCNHiProxyAPI } from "providers"; +import React, { useEffect, useState } from "react"; +import PageContainer from "./PageContainer"; + +export default function CNHi() { + const [currentOrg, setCurrentOrg] = useState(""); + const cnhiAPI = useCNHiProxyAPI(); + const [organizations, setOrganizations] = useState>(new Map()); + const [fieldAccordion, setFieldAccordion] = useState(false); + const [dataOptions, setDataOptions] = useState([]); + const [{ as }] = useGlobalState(); + + //load organizations for the user + useEffect(() => { + setCurrentOrg(""); + cnhiAPI + .listAccounts(0, 0, as) + .then(resp => { + let tempOrgs: Map = new Map(); + resp.data.accounts.forEach(org => { + let organization = pond.JDAccount.fromObject(org); + tempOrgs.set(organization.key, organization); + }); + setOrganizations(tempOrgs); + }) + .catch(err => {}); + }, [cnhiAPI, as]); + + useEffect(() => { + let organization = organizations.get(currentOrg); + if (organization) { + let currentOptions: pond.DataOption[] = organization.options ?? []; + setDataOptions(currentOptions); + } + }, [currentOrg, organizations]); + + const updateOrgData = (checked: boolean, option: pond.DataOption) => { + let currentOps: pond.DataOption[] = dataOptions; + if (checked && !currentOps.includes(option)) { + currentOps.push(option); + } else if (!checked && currentOps.includes(option)) { + currentOps.splice(currentOps.indexOf(option), 1); + } + setDataOptions([...currentOps]); + }; + + const submit = () => { + cnhiAPI.updateAccount(currentOrg, dataOptions, as ?? undefined).then(resp => { + //update the organization in the map to have the correct dataOptions + let org = organizations.get(currentOrg); + if (org) { + org.options = dataOptions; + } + }); + }; + + const fieldOptions = () => { + return ( + + { + setFieldAccordion(expanded); + }}> + }>Field Data + + + + { + //setFields(!fields); + updateOrgData(checked, pond.DataOption.DATA_OPTION_FIELDS); + }} + /> + } + /> + + + View Field Boundaries from Case New Holland on your visual Farm and set up harvest + plans and track tasks + + + + + + ); + }; + return ( + + + + + + + + + + + {fieldOptions()} + + ); +} diff --git a/src/pages/JohnDeere.tsx b/src/pages/JohnDeere.tsx new file mode 100644 index 0000000..b2d57f6 --- /dev/null +++ b/src/pages/JohnDeere.tsx @@ -0,0 +1,453 @@ +import { + Accordion, + AccordionDetails, + AccordionSummary, + Box, + Button, + Checkbox, + FormControlLabel, + Grid2 as Grid, + MenuItem, + Select +} from "@mui/material"; +import { ExpandMore } from "@mui/icons-material"; +import JDAccess from "integrations/JohnDeere/JDAccess"; +import { pond } from "protobuf-ts/pond"; +import { useGlobalState } from "providers"; +import { useJohnDeereProxyAPI } from "providers/pond/johnDeereProxyAPI"; +import React, { useEffect, useState } from "react"; +import PageContainer from "./PageContainer"; + +export default function JohnDeere() { + const jdAPI = useJohnDeereProxyAPI(); + const [{ as }] = useGlobalState(); + const [organizations, setOrganizations] = useState>(new Map()); + const [currentOrg, setCurrentOrg] = useState(""); + //booleans for what is selected for import with the jd organization + //const [fields, setFields] = useState(false); + const [dataOptions, setDataOptions] = useState([]); + + //accordions status + const [fieldAccordion, setFieldAccordion] = useState(false); + // const [equipmentAccordion, setEquipmentAccordion] = useState(false); + // const [additivesAccordion, setAdditivesAccordion] = useState(false); + // const [managementAccordion, setManagementAccordion] = useState(false); + + //load organizations for the user + useEffect(() => { + setCurrentOrg(""); + jdAPI + .listAccounts(0, 0, as) + .then(resp => { + let tempOrgs: Map = new Map(); + resp.data.accounts.forEach(org => { + let organization = pond.JDAccount.fromObject(org); + tempOrgs.set(organization.key, organization); + }); + setOrganizations(tempOrgs); + }) + .catch(err => {}); + }, [jdAPI, as]); + + useEffect(() => { + let organization = organizations.get(currentOrg); + if (organization) { + let currentOptions: pond.DataOption[] = organization.options ?? []; + setDataOptions(currentOptions); + } + }, [currentOrg, organizations]); + + const updateOrgData = (checked: boolean, option: pond.DataOption) => { + // let organization = organizations.get(currentOrg); + // if (organization) { + // if (checked && !organization.options.includes(option)) { + // organization.options.push(option); + // } else if (!checked && organization.options.includes(option)) { + // organization.options.splice(organization.options.indexOf(option), 1); + // } + // } + let currentOps: pond.DataOption[] = dataOptions; + if (checked && !currentOps.includes(option)) { + currentOps.push(option); + } else if (!checked && currentOps.includes(option)) { + currentOps.splice(currentOps.indexOf(option), 1); + } + setDataOptions([...currentOps]); + }; + + const submit = () => { + jdAPI.updateAccount(currentOrg, dataOptions, as ?? undefined).then(resp => { + //update the organization in the map to have the correct dataOptions + let org = organizations.get(currentOrg); + if (org) { + org.options = dataOptions; + } + }); + }; + + const fieldOptions = () => { + return ( + + { + setFieldAccordion(expanded); + }}> + }>Field Data + + + + { + //setFields(!fields); + updateOrgData(checked, pond.DataOption.DATA_OPTION_FIELDS); + }} + /> + } + /> + + + View Field Boundaries from John Deere and set up harvest plans and track tasks + + {/* not yet implemented in the backend */} + {/* + { + updateOrgData(checked, pond.DataOption.DATA_OPTION_FLAGS); + }} + /> + } + /> + + + Plots your flags as markers on your Visual Farm + */} + + + + + ); + }; + + // const equipmentOptions = () => { + // return ( + // + // { + // setEquipmentAccordion(expanded); + // }}> + // }>Equipment + // + // + // + // { + // updateOrgData(checked, pond.DataOption.DATA_OPTION_MACHINES); + // }} + // /> + // } + // /> + // + // + // View information about your machinery from John Deere + // + // + // { + // updateOrgData(checked, pond.DataOption.DATA_OPTION_IMPLEMENTS); + // }} + // /> + // } + // /> + // + // + // Implement Information + // + // + // { + // updateOrgData(checked, pond.DataOption.DATA_OPTION_MAINTENENCE); + // }} + // /> + // } + // /> + // + // + // Equipment Maitenance details + // + // + // + // + // + // ); + // }; + + // const additivesOptions = () => { + // return ( + // + // { + // setAdditivesAccordion(expanded); + // }}> + // }>Additives + // + // + // + // { + // updateOrgData(checked, pond.DataOption.DATA_OPTION_CHEMICALS); + // }} + // /> + // } + // /> + // + // + // View information about chemicals sprayed on fields + // + // + // { + // updateOrgData(checked, pond.DataOption.DATA_OPTION_VARIETIES); + // }} + // /> + // } + // /> + // + // + // Not sure what these are, may be grain varieties in which case this should go into + // the field data most likely + // + // + // { + // updateOrgData(checked, pond.DataOption.DATA_OPTION_FERTILIZER); + // }} + // /> + // } + // /> + // + // + // Information about fetilizer used on fields + // + // + // + // + // + // ); + // }; + + // const managementOptions = () => { + // return ( + // + // { + // setManagementAccordion(expanded); + // }}> + // }>Management + // + // + // + // { + // updateOrgData(checked, pond.DataOption.DATA_OPTION_STAFF); + // }} + // /> + // } + // /> + // + // + // View information about farm staff + // + // + // { + // updateOrgData(checked, pond.DataOption.DATA_OPTION_FILES); + // }} + // /> + // } + // /> + // + // + // Not sure what this pulls + // + // + // { + // updateOrgData(checked, pond.DataOption.DATA_OPTION_TASKS); + // }} + // /> + // } + // /> + // + // + // Pull tasks into our task management system + // + // + // { + // updateOrgData(checked, pond.DataOption.DATA_OPTION_NOTIFICATIONS); + // }} + // /> + // } + // /> + // + // + // View Notifications and events + // + // + // { + // updateOrgData(checked, pond.DataOption.DATA_OPTION_ASSETS); + // }} + // /> + // } + // /> + // + // + // View Assets registered with john deere + // + // + // { + // updateOrgData(checked, pond.DataOption.DATA_OPTION_OPERATORS); + // }} + // /> + // } + // /> + // + // + // View Operators + // + // + // { + // updateOrgData(checked, pond.DataOption.DATA_OPTION_CLIENTS); + // }} + // /> + // } + // /> + // + // + // View Client information + // + // + // + // + // + // ); + // }; + + return ( + + + + + + + + + + + + + {/* Field data accordion */} + {fieldOptions()} + {/* these three are not yet implemented TODO: reveal these options once implemented in the backend */} + {/* + {equipmentOptions()} + {additivesOptions()} + {managementOptions()} + */} + + + ); +} diff --git a/src/pages/Users.tsx b/src/pages/Users.tsx index 5e15a3c..00ef9f7 100644 --- a/src/pages/Users.tsx +++ b/src/pages/Users.tsx @@ -127,7 +127,8 @@ export default function Users() { "grain-composition", "developer", "marketplace", - "installer" + "installer", + "cnhi" ].sort(); const [rows, setRows] = useState([]) diff --git a/src/providers/index.ts b/src/providers/index.ts index dafcf34..3eb9ab5 100644 --- a/src/providers/index.ts +++ b/src/providers/index.ts @@ -43,7 +43,9 @@ export { useGrainBagAPI, useTransactionAPI, useFileControllerAPI, - useContractAPI //TODO: update api with resolve, reject + useContractAPI, //TODO: update api with resolve, reject + useJohnDeereProxyAPI, //TODO: update api with resolve, reject + useCNHiProxyAPI //TODO: update api with resolve, reject } from "./pond/pond"; // export { SecurityContext, useSecurity } from "./security"; export { SnackbarContext, useSnackbar } from "./Snackbar"; diff --git a/src/providers/pond/cnhiProxyAPI.tsx b/src/providers/pond/cnhiProxyAPI.tsx new file mode 100644 index 0000000..3642783 --- /dev/null +++ b/src/providers/pond/cnhiProxyAPI.tsx @@ -0,0 +1,124 @@ +import { AxiosResponse } from "axios"; +import { useHTTP } from "hooks"; +import { pond } from "protobuf-ts/pond"; +import { createContext, PropsWithChildren, useContext } from "react"; +//import { or } from "utils"; +import { pondURL } from "./pond"; + +export interface ICNHiProxyAPIContext { + //add new organization + addAccount: ( + teamKey: string, + userID: string, + cnhiCode: string, + cnhiUsername: string + ) => Promise>; + //list organizations + listAccounts: ( + limit: number, + offset: number, + as?: string, + keys?: string[], + types?: string[] + ) => Promise>; + updateAccount: ( + key: string, + options: pond.DataOption[], + as?: string + ) => Promise>; + listFields: ( + limit: number, + offset: number, + // order?: "asc" | "desc", + // orderBy?: string, + // search?: string, + as?: string, + asRoot?: boolean + ) => Promise>; +} + +export const CNHiProxyAPIContext = createContext({} as ICNHiProxyAPIContext); + +interface Props {} + +export default function CNHiProvider(props: PropsWithChildren) { + const { children } = props; + const { post, get, put } = useHTTP(); + + const addAccount = (teamKey: string, userID: string, cnhiCode: string, cnhiUsername: string) => { + return post( + pondURL( + "/cnhiAccounts?team=" + + teamKey + + "&user=" + + userID + + "&code=" + + cnhiCode + + "&cnhiUsername=" + + cnhiUsername + ) + ); + }; + + const listAccounts = ( + limit: number, + offset: number, + as?: string, + keys?: string[], + types?: string[] + ) => { + return get( + pondURL( + "/cnhiAccounts?limit=" + + limit + + "&offset=" + + offset + + (as ? "&as=" + as : "") + + (keys ? "&keys=" + keys.join(",") : "") + + (types ? "&types=" + types.join(",") : "") + ) + ); + }; + + const listFields = ( + limit: number, + offset: number, + // order?: "asc" | "desc", + // orderBy?: string, + // search?: string, + as?: string, + asRoot?: boolean + ) => { + return get( + pondURL( + "/cnhiFields" + + "?limit=" + + limit + + "&offset=" + + offset + + (as ? "&as=" + as : "") + + (asRoot ? "&asRoot=" + asRoot.toString() : "") + ) + ); + }; + + const updateAccount = (key: string, options: pond.DataOption[], as?: string) => { + return put( + pondURL("/cnhiAccounts/" + key + "?options=" + options.toString() + (as ? "&as=" + as : "")) + ); + }; + + return ( + + {children} + + ); +} + +export const useCNHiProxyAPI = () => useContext(CNHiProxyAPIContext); diff --git a/src/providers/pond/johnDeereProxyAPI.tsx b/src/providers/pond/johnDeereProxyAPI.tsx new file mode 100644 index 0000000..4042292 --- /dev/null +++ b/src/providers/pond/johnDeereProxyAPI.tsx @@ -0,0 +1,127 @@ +import { AxiosResponse } from "axios"; +import { useHTTP } from "hooks"; +import { pond } from "protobuf-ts/pond"; +import { createContext, PropsWithChildren, useContext } from "react"; +//import { or } from "utils"; +import { pondURL } from "./pond"; + +export interface IJohnDeereProxyAPIContext { + //add new organization + addAccount: ( + teamKey: string, + userID: string, + jdCode: string, + jdUserName: string + ) => Promise>; + //list organizations + listAccounts: ( + limit: number, + offset: number, + as?: string, + keys?: string[], + types?: string[] + ) => Promise>; + updateAccount: ( + key: string, + options: pond.DataOption[], + as?: string + ) => Promise>; + //update organizations + listFields: ( + limit: number, + offset: number, + // order?: "asc" | "desc", + // orderBy?: string, + // search?: string, + as?: string, + asRoot?: boolean + ) => Promise>; +} + +export const JohnDeereProxyAPIContext = createContext( + {} as IJohnDeereProxyAPIContext +); + +interface Props {} + +export default function JohnDeereProvider(props: PropsWithChildren) { + const { children } = props; + const { post, get, put } = useHTTP(); + + const addAccount = (teamKey: string, userID: string, jdCode: string, jdUserName: string) => { + return post( + pondURL( + "/jdAccounts?team=" + + teamKey + + "&user=" + + userID + + "&code=" + + jdCode + + "&jdUserName=" + + jdUserName + ) + ); + }; + + const listAccounts = ( + limit: number, + offset: number, + as?: string, + keys?: string[], + types?: string[] + ) => { + return get( + pondURL( + "/jdAccounts?limit=" + + limit + + "&offset=" + + offset + + (as ? "&as=" + as : "") + + (keys ? "&keys=" + keys.join(",") : "") + + (types ? "&types=" + types.join(",") : "") + ) + ); + }; + + const updateAccount = (key: string, options: pond.DataOption[], as?: string) => { + return put( + pondURL("/jdAccounts/" + key + "?options=" + options.toString() + (as ? "&as=" + as : "")) + ); + }; + + const listFields = ( + limit: number, + offset: number, + // order?: "asc" | "desc", + // orderBy?: string, + // search?: string, + as?: string, + asRoot?: boolean + ) => { + return get( + pondURL( + "/jdFields" + + "?limit=" + + limit + + "&offset=" + + offset + + (as ? "&as=" + as : "") + + (asRoot ? "&asRoot=" + asRoot.toString() : "") + ) + ); + }; + + return ( + + {children} + + ); +} + +export const useJohnDeereProxyAPI = () => useContext(JohnDeereProxyAPIContext); diff --git a/src/providers/pond/pond.tsx b/src/providers/pond/pond.tsx index bcaeaa5..784994a 100644 --- a/src/providers/pond/pond.tsx +++ b/src/providers/pond/pond.tsx @@ -32,6 +32,8 @@ import PreferenceProvider, {usePreferenceAPI} from "./preferenceAPI"; import TaskProvider, { useTaskAPI } from "./taskAPI"; import DataDogProvider, { useDataDogProxyAPI } from "./datadogProxyAPI"; import ContractProvider, { useContractAPI } from "./contractAPI"; +import JohnDeereProvider, { useJohnDeereProxyAPI } from "./johnDeereProxyAPI"; +import CNHiProvider, { useCNHiProxyAPI } from "./cnhiProxyAPI"; // import NoteProvider from "providers/noteAPI"; export const pondURL = (partial: string, demo: boolean = false): string => { @@ -80,7 +82,11 @@ export default function PondProvider(props: PropsWithChildren) { - {children} + + + {children} + + @@ -147,5 +153,7 @@ export { usePreferenceAPI, useTaskAPI, useDataDogProxyAPI, - useContractAPI + useContractAPI, + useJohnDeereProxyAPI, + useCNHiProxyAPI };