From a0a54bee2aee697d957fe792c326fc2a4849b044 Mon Sep 17 00:00:00 2001 From: csawatzky Date: Thu, 17 Jul 2025 13:42:11 -0600 Subject: [PATCH 1/8] moved all of the libracart suff from the old frontend into the new new, also added libracart as an option alongside jd and cnh to skip the auth0 callback when redirected back to us from there --- package-lock.json | 4 +- package.json | 2 +- src/app/App.tsx | 3 +- .../marketplaceImages/LibraCartGrey.png | Bin 0 -> 45281 bytes .../LibraCart/LibraCartAccess.tsx | 178 ++++++++++++++++++ src/navigation/Router.tsx | 4 + src/navigation/SideNavigator.tsx | 15 ++ src/pages/CNHi.tsx | 2 +- src/pages/LibraCart.tsx | 148 +++++++++++++++ src/pages/Team.tsx | 2 +- src/products/CommonIcons/libracartIcon.tsx | 24 +++ src/providers/pond/libracartProxyAPI.tsx | 133 +++++++++++++ src/providers/pond/pond.tsx | 16 +- src/userFeatures/UserFeatures.tsx | 9 + 14 files changed, 528 insertions(+), 12 deletions(-) create mode 100644 src/assets/marketplaceImages/LibraCartGrey.png create mode 100644 src/integrations/LibraCart/LibraCartAccess.tsx create mode 100644 src/pages/LibraCart.tsx create mode 100644 src/products/CommonIcons/libracartIcon.tsx create mode 100644 src/providers/pond/libracartProxyAPI.tsx diff --git a/package-lock.json b/package-lock.json index 1d88f79..bcf258c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,7 +42,7 @@ "mui-tel-input": "^7.0.0", "notistack": "^3.0.1", "openweathermap-ts": "^1.2.10", - "protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#staging", + "protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#libracart", "query-string": "^9.2.1", "react": "^18.3.1", "react-beautiful-dnd": "^13.1.1", @@ -10889,7 +10889,7 @@ }, "node_modules/protobuf-ts": { "version": "1.0.0", - "resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#4523ffef42032eb4a923bd50d4cfcccbb1397dbd", + "resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#174342d58e3d36bda2f2efc08aa3c95a217125e1", "dependencies": { "protobufjs": "^6.8.8" } diff --git a/package.json b/package.json index af322fc..3e0ee84 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "mui-tel-input": "^7.0.0", "notistack": "^3.0.1", "openweathermap-ts": "^1.2.10", - "protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#staging", + "protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#libracart", "query-string": "^9.2.1", "react": "^18.3.1", "react-beautiful-dnd": "^13.1.1", diff --git a/src/app/App.tsx b/src/app/App.tsx index 16bb781..2c6b94b 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -50,7 +50,8 @@ function App() { const skipCallbacks = [ "/johndeere", - "/cnhi" + "/cnhi", + "/libracart" ] return ( diff --git a/src/assets/marketplaceImages/LibraCartGrey.png b/src/assets/marketplaceImages/LibraCartGrey.png new file mode 100644 index 0000000000000000000000000000000000000000..331ac297778c42435f0e66f276fb771917f78b67 GIT binary patch literal 45281 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⪼^)J{g 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 literal 0 HcmV?d00001 diff --git a/src/integrations/LibraCart/LibraCartAccess.tsx b/src/integrations/LibraCart/LibraCartAccess.tsx new file mode 100644 index 0000000..738ce13 --- /dev/null +++ b/src/integrations/LibraCart/LibraCartAccess.tsx @@ -0,0 +1,178 @@ +import { + Avatar, + Box, + Button, + DialogActions, + DialogContent, + DialogTitle, + 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 } from "providers"; +import { useLibraCartProxyAPI } from "providers/pond/libracartProxyAPI"; +import React, { useEffect, useState } from "react"; +import { useLocation } from "react-router"; +import TeamSearch from "teams/TeamSearch"; + +export default function LibraCartAccess() { + const [openDialog, setOpenDialog] = useState(false); + const [teamKey, setTeamKey] = useState(""); + const [teamUsers, setTeamUsers] = useState([]); + const [primaryUser, setPrimaryUser] = useState(""); + const [libracartUsername, setLibraCartUserName] = useState(""); + const [libracartCode, setLibraCartCode] = useState(""); + //const [activeStep, setActiveStep] = useState(0); + const userAPI = useUserAPI(); + const libracartAPI = useLibraCartProxyAPI(); + //const [dataOps, setDataOps] = useState([]); + const { openSnack } = useSnackbar(); + const search = useLocation().search; + const searchParams = new URLSearchParams(search); + //const [{ as }] = useGlobalState(); + // const integration_url = process.env.REACT_APP_LIBRACART_INTEGRATION_URL; + const integration_url = import.meta.env.VITE_LIBRACART_INTEGRATION_URL; + + + const submitNewOrganization = () => { + if (libracartCode) { + libracartAPI + .addAccount(teamKey, primaryUser, libracartCode, libracartUsername) + .then(resp => { + //the code was used so remove it from local storage + if (localStorage.getItem("code")) { + localStorage.removeItem("code"); + } + openSnack("Added New Libra Cart Account Link"); + }) + .catch(err => { + openSnack("Failed to Add New Libra Cart 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("state"); + if (code) { + setLibraCartCode(code); + setOpenDialog(true); + } + }, [searchParams, libracartCode]); + + const validate = () => { + let invalid = false; + if (libracartUsername === "" || teamKey === "" || primaryUser === "" || libracartCode === "") { + invalid = true; + } + return invalid; + }; + + const general = () => { + return ( + + setLibraCartUserName(e.target.value)} + /> + + setPrimaryUser(e.target.value)}> + {teamUsers.map(u => ( + + + + + + + {u.name()} + + + + ))} + + setLibraCartCode(e.target.value)} + /> + + ); + }; + + const newOrgDialog = () => { + return ( + { + setOpenDialog(false); + }}> + Enter New Integration Link + {general()} + + + + + + ); + }; + + return ( + + + To integrate with Libra Cart, navigate to Integrations page on Libra Cart and "Connect" to + Adaptive Agriculture App: https://staging.cloud.agrimatics.com/grain/integrations + {newOrgDialog()} + + ); +} diff --git a/src/navigation/Router.tsx b/src/navigation/Router.tsx index 29362ad..1f2ed7d 100644 --- a/src/navigation/Router.tsx +++ b/src/navigation/Router.tsx @@ -45,6 +45,7 @@ const Contracts = lazy(() => import("pages/Contracts")); const Contract = lazy(() => import("pages/Contract")); const JohnDeere = lazy(() => import("pages/JohnDeere")); const CNHi = lazy(() => import("pages/CNHi")); +const LibraCart = lazy(() => import("pages/LibraCart")); export const appendToUrl = (appendage: number | string) => { const basePath = location.pathname.replace(/\/$/, ""); @@ -337,6 +338,9 @@ export default function Router() { {user.hasFeature("cnhi") && } /> } + {user.hasFeature("libra-cart") && + } /> + } {/* Map routes */} } /> } /> diff --git a/src/navigation/SideNavigator.tsx b/src/navigation/SideNavigator.tsx index 38a10cb..f42d5b1 100644 --- a/src/navigation/SideNavigator.tsx +++ b/src/navigation/SideNavigator.tsx @@ -48,6 +48,7 @@ import DataDuckIcon from "products/Bindapt/DataDuckIcon"; import ContractsIcon from "products/CommonIcons/contractIcon"; import JohnDeereIcon from "products/CommonIcons/johnDeereIcon"; import CNHiIcon from "products/CommonIcons/cnhiIcon"; +import LibraCartIcon from "products/CommonIcons/libracartIcon"; const drawerWidth = 230; @@ -477,6 +478,20 @@ export default function SideNavigator(props: Props) { {open && } + } + {user.hasFeature("libra-cart") && + + goTo("/libracart")} + classes={getClasses("/libracart")} + > + + + + {open && } + + } ) diff --git a/src/pages/CNHi.tsx b/src/pages/CNHi.tsx index 24be24c..260d614 100644 --- a/src/pages/CNHi.tsx +++ b/src/pages/CNHi.tsx @@ -20,7 +20,7 @@ import PageContainer from "./PageContainer"; export default function CNHi() { const [currentOrg, setCurrentOrg] = useState(""); const cnhiAPI = useCNHiProxyAPI(); - const [organizations, setOrganizations] = useState>(new Map()); + const [organizations, setOrganizations] = useState>(new Map()); const [fieldAccordion, setFieldAccordion] = useState(false); const [dataOptions, setDataOptions] = useState([]); const [{ as }] = useGlobalState(); diff --git a/src/pages/LibraCart.tsx b/src/pages/LibraCart.tsx new file mode 100644 index 0000000..5fc06eb --- /dev/null +++ b/src/pages/LibraCart.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 LibraCartAccess from "integrations/LibraCart/LibraCartAccess"; +import { pond } from "protobuf-ts/pond"; +import { useGlobalState } from "providers"; +import { useLibraCartProxyAPI } from "providers/pond/libracartProxyAPI"; +import React, { useEffect, useState } from "react"; +import PageContainer from "./PageContainer"; + +export default function LibraCart() { + const [currentOrg, setCurrentOrg] = useState(""); + const libracartAPI = useLibraCartProxyAPI(); + 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(""); + libracartAPI + .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 => {}); + }, [libracartAPI, 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 = () => { + libracartAPI.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 your data from Libra Cart on your visual Farm + + + + + + ); + }; + return ( + + + + + + + + + + + {fieldOptions()} + + ); +} diff --git a/src/pages/Team.tsx b/src/pages/Team.tsx index 7a8f29f..dd88fdd 100644 --- a/src/pages/Team.tsx +++ b/src/pages/Team.tsx @@ -214,7 +214,7 @@ export default function TeamPage() { } return ( - + {title()} diff --git a/src/products/CommonIcons/libracartIcon.tsx b/src/products/CommonIcons/libracartIcon.tsx new file mode 100644 index 0000000..c71c55a --- /dev/null +++ b/src/products/CommonIcons/libracartIcon.tsx @@ -0,0 +1,24 @@ +// update logo images when they share official black and white +import LibraCartLogoWhite from "assets/marketplaceImages/LibraCartGrey.png"; +import LibraCartLogoBlack from "assets/marketplaceImages/LibraCartGrey.png"; +import { ImgIcon } from "common/ImgIcon"; +import { useThemeType } from "hooks"; + +interface Props { + type?: "light" | "dark"; +} + +export default function LibraCartIcon(props: Props) { + const themeType = useThemeType(); + const { type } = props; + + const src = () => { + if (type) { + return type === "light" ? LibraCartLogoWhite : LibraCartLogoBlack; + } + + return themeType === "light" ? LibraCartLogoBlack : LibraCartLogoWhite; + }; + + return ; +} \ No newline at end of file diff --git a/src/providers/pond/libracartProxyAPI.tsx b/src/providers/pond/libracartProxyAPI.tsx new file mode 100644 index 0000000..28f41e6 --- /dev/null +++ b/src/providers/pond/libracartProxyAPI.tsx @@ -0,0 +1,133 @@ +import { AxiosResponse } from "axios"; +import { useHTTP } from "hooks"; +import { pond } from "protobuf-ts/pond"; +import React, { createContext, PropsWithChildren, useContext } from "react"; +//import { or } from "utils"; +import { pondURL } from "./pond"; + +export interface ILibraCartProxyAPIContext { + //add new organization + addAccount: ( + teamKey: string, + userID: string, + libracartCode: string, + libracartUsername: 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 LibraCartProxyAPIContext = createContext( + {} as ILibraCartProxyAPIContext +); + +interface Props {} + +export default function LibraCartProvider(props: PropsWithChildren) { + const { children } = props; + const { post, get, put } = useHTTP(); + + const addAccount = ( + teamKey: string, + userID: string, + libracartCode: string, + libracartUsername: string + ) => { + return post( + pondURL( + "/libracartAccounts?team=" + + teamKey + + "&user=" + + userID + + "&code=" + + libracartCode + + "&libracartUsername=" + + libracartUsername + ) + ); + }; + + const listAccounts = ( + limit: number, + offset: number, + as?: string, + keys?: string[], + types?: string[] + ) => { + return get( + pondURL( + "/libracartAccounts?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( + "/libracartFields" + + "?limit=" + + limit + + "&offset=" + + offset + + (as ? "&as=" + as : "") + + (asRoot ? "&asRoot=" + asRoot.toString() : "") + ) + ); + }; + + const updateAccount = (key: string, options: pond.DataOption[], as?: string) => { + return put( + pondURL( + "/libracartAccounts/" + key + "?options=" + options.toString() + (as ? "&as=" + as : "") + ) + ); + }; + + return ( + + {children} + + ); +} + +export const useLibraCartProxyAPI = () => useContext(LibraCartProxyAPIContext); diff --git a/src/providers/pond/pond.tsx b/src/providers/pond/pond.tsx index 8e879f9..0efc34d 100644 --- a/src/providers/pond/pond.tsx +++ b/src/providers/pond/pond.tsx @@ -35,6 +35,7 @@ import ContractProvider, { useContractAPI } from "./contractAPI"; import UsageProvider, { useUsageAPI } from "./usageAPI"; import KeyManagerProvider, { useKeyManagerAPI } from "./keyManagerAPI"; import JohnDeereProvider, { useJohnDeereProxyAPI } from "./johnDeereProxyAPI"; +import LibraCartProvider, { useLibraCartProxyAPI } from "./libracartProxyAPI"; import CNHiProvider, { useCNHiProxyAPI } from "./cnhiProxyAPI"; // import NoteProvider from "providers/noteAPI"; @@ -86,11 +87,13 @@ export default function PondProvider(props: PropsWithChildren) { - - - {children} - - + + + + {children} + + + @@ -163,5 +166,6 @@ export { useUsageAPI, useKeyManagerAPI, useJohnDeereProxyAPI, - useCNHiProxyAPI + useCNHiProxyAPI, + useLibraCartProxyAPI }; diff --git a/src/userFeatures/UserFeatures.tsx b/src/userFeatures/UserFeatures.tsx index 9f9a2f4..54aba25 100644 --- a/src/userFeatures/UserFeatures.tsx +++ b/src/userFeatures/UserFeatures.tsx @@ -5,6 +5,7 @@ import { useSnackbar, useUserAPI } from "hooks"; import React, { useEffect, useState } from "react"; import JohnDeereIcon from "products/CommonIcons/johnDeereIcon"; import CNHiIcon from "products/CommonIcons/cnhiIcon"; +import LibraCartIcon from "products/CommonIcons/libracartIcon"; //import AgLogo from "assets/whitelabels/AdaptiveAgriculture/AGLogoSquare.png"; import { IsAdaptiveAgriculture @@ -60,6 +61,14 @@ const agFeatureList: ProductDetails[] = [ "Integrate with the Case New Holland Industrial Center to bring your data into our platform" // bulletPoints: ["bullet one", "bullet two"], // questions: [], + }, + { + featureName: "libra-cart", + featureLogo: , + featureCost: 0, + cardImage: FeatureImageTest, + featureTitle: "Libra Cart", + longDescription: "Integrate with Libra Cart to bring your data into our platform" } ]; From 767ca5ef9795ed2d47901ad8846634ba483e937b Mon Sep 17 00:00:00 2001 From: csawatzky Date: Fri, 18 Jul 2025 11:01:34 -0600 Subject: [PATCH 2/8] update to libracart access to pull the data from the url --- src/integrations/LibraCart/LibraCartAccess.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/integrations/LibraCart/LibraCartAccess.tsx b/src/integrations/LibraCart/LibraCartAccess.tsx index 738ce13..d2a3433 100644 --- a/src/integrations/LibraCart/LibraCartAccess.tsx +++ b/src/integrations/LibraCart/LibraCartAccess.tsx @@ -63,13 +63,21 @@ export default function LibraCartAccess() { } }, [teamKey, userAPI]); + // useEffect(() => { + // let code = localStorage.getItem("state"); + // if (code) { + // setLibraCartCode(code); + // setOpenDialog(true); + // } + // }, [searchParams, libracartCode]); + useEffect(() => { - let code = localStorage.getItem("state"); + let code = new URLSearchParams(window.location.search).get("state"); // not sure if the state is correct here i thought it should be the access_token if (code) { setLibraCartCode(code); setOpenDialog(true); } - }, [searchParams, libracartCode]); + }, [window.location]); const validate = () => { let invalid = false; From 093ad7b2ad7461895cce3d2d1fdf64f8fe4bf239 Mon Sep 17 00:00:00 2001 From: csawatzky Date: Fri, 18 Jul 2025 13:46:07 -0600 Subject: [PATCH 3/8] sending the auth from libracart to the add endpoints --- package-lock.json | 2 +- .../LibraCart/LibraCartAccess.tsx | 27 +++++++++++-------- src/providers/pond/libracartProxyAPI.tsx | 4 +++ 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index bcf258c..d6134d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10889,7 +10889,7 @@ }, "node_modules/protobuf-ts": { "version": "1.0.0", - "resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#174342d58e3d36bda2f2efc08aa3c95a217125e1", + "resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#e1a765ee24fd1c6e374fd6d7a21edb82feb45c6f", "dependencies": { "protobufjs": "^6.8.8" } diff --git a/src/integrations/LibraCart/LibraCartAccess.tsx b/src/integrations/LibraCart/LibraCartAccess.tsx index d2a3433..391a3fa 100644 --- a/src/integrations/LibraCart/LibraCartAccess.tsx +++ b/src/integrations/LibraCart/LibraCartAccess.tsx @@ -25,27 +25,22 @@ export default function LibraCartAccess() { const [primaryUser, setPrimaryUser] = useState(""); const [libracartUsername, setLibraCartUserName] = useState(""); const [libracartCode, setLibraCartCode] = useState(""); + const [libracartAuth, setLibraCartAuth] = useState(""); //const [activeStep, setActiveStep] = useState(0); const userAPI = useUserAPI(); const libracartAPI = useLibraCartProxyAPI(); //const [dataOps, setDataOps] = useState([]); const { openSnack } = useSnackbar(); - const search = useLocation().search; - const searchParams = new URLSearchParams(search); //const [{ as }] = useGlobalState(); // const integration_url = process.env.REACT_APP_LIBRACART_INTEGRATION_URL; const integration_url = import.meta.env.VITE_LIBRACART_INTEGRATION_URL; const submitNewOrganization = () => { - if (libracartCode) { + if (libracartCode && libracartAuth) { libracartAPI - .addAccount(teamKey, primaryUser, libracartCode, libracartUsername) + .addAccount(teamKey, primaryUser, libracartCode, libracartAuth, libracartUsername) .then(resp => { - //the code was used so remove it from local storage - if (localStorage.getItem("code")) { - localStorage.removeItem("code"); - } openSnack("Added New Libra Cart Account Link"); }) .catch(err => { @@ -72,9 +67,12 @@ export default function LibraCartAccess() { // }, [searchParams, libracartCode]); useEffect(() => { - let code = new URLSearchParams(window.location.search).get("state"); // not sure if the state is correct here i thought it should be the access_token - if (code) { + let code = new URLSearchParams(window.location.search).get("state"); // this is the account_code + let auth = new URLSearchParams(window.location.search).get("authorization_token"); // this is the authorization used to verify where it came from + + if (code && auth) { setLibraCartCode(code); + setLibraCartAuth(auth); setOpenDialog(true); } }, [window.location]); @@ -133,7 +131,14 @@ export default function LibraCartAccess() { fullWidth variant="outlined" value={libracartCode} - onChange={e => setLibraCartCode(e.target.value)} + /> + ); diff --git a/src/providers/pond/libracartProxyAPI.tsx b/src/providers/pond/libracartProxyAPI.tsx index 28f41e6..3b056c1 100644 --- a/src/providers/pond/libracartProxyAPI.tsx +++ b/src/providers/pond/libracartProxyAPI.tsx @@ -11,6 +11,7 @@ export interface ILibraCartProxyAPIContext { teamKey: string, userID: string, libracartCode: string, + libracartAuth: string, libracartUsername: string ) => Promise>; //list organizations @@ -51,6 +52,7 @@ export default function LibraCartProvider(props: PropsWithChildren) { teamKey: string, userID: string, libracartCode: string, + libracartAuth: string, libracartUsername: string ) => { return post( @@ -61,6 +63,8 @@ export default function LibraCartProvider(props: PropsWithChildren) { userID + "&code=" + libracartCode + + "&auth=" + + libracartAuth + "&libracartUsername=" + libracartUsername ) From ad479e2b452d2b17e7b56d05f7ed1a487c06372e Mon Sep 17 00:00:00 2001 From: csawatzky Date: Tue, 5 Aug 2025 10:34:20 -0600 Subject: [PATCH 4/8] added in the device preset stuff for bin modes since it was missed in the initial update to the new platform --- src/device/presets/devicePresetCard.tsx | 108 ++++++++--------- src/providers/pond/devicePresetAPI.tsx | 150 ++++++++++++++++++++++++ src/providers/pond/pond.tsx | 8 +- 3 files changed, 210 insertions(+), 56 deletions(-) create mode 100644 src/providers/pond/devicePresetAPI.tsx diff --git a/src/device/presets/devicePresetCard.tsx b/src/device/presets/devicePresetCard.tsx index b01def2..2839df3 100644 --- a/src/device/presets/devicePresetCard.tsx +++ b/src/device/presets/devicePresetCard.tsx @@ -6,7 +6,7 @@ import { DialogActions, DialogContent, DialogTitle, - Grid, + Grid2 as Grid, InputAdornment, MenuItem, Slider, @@ -19,7 +19,7 @@ import { DevicePreset } from "models/DevicePreset"; import { ObjectTypeString } from "objects/ObjectDescriber"; import { pond } from "protobuf-ts/pond"; import { useSnackbar } from "providers"; -// import { useDevicePresetAPI } from "providers/pond/devicePresetAPI"; +import { useDevicePresetAPI } from "providers/pond/devicePresetAPI"; import React, { useEffect, useState } from "react"; import { getTemperatureUnit } from "utils"; @@ -43,7 +43,7 @@ export default function DevicePresetCard(props: Props) { removeCallback, updateCallback } = props; - // const devicePresetAPI = useDevicePresetAPI(); + const devicePresetAPI = useDevicePresetAPI(); const [presetName, setPresetName] = useState(""); const [presetType, setPresetType] = useState(1); const [controllerType, setControllerType] = useState(1); @@ -81,22 +81,22 @@ export default function DevicePresetCard(props: Props) { p.settings.temperature = tempC; p.settings.humidity = hum; - // devicePresetAPI - // .addDevicePreset(p.name, p.settings, objectKey, typestring) - // .then(resp => { - // //get the temp key from the preset - // let temp = p.key; - // //update the key in the preset with the key that came back from the add - // p.key = resp.data.key; - // //use the callback function passing in the preset and the temp key - // if (addCallback) { - // addCallback(p, temp); - // } - // openSnack("New preset saved"); - // }) - // .catch(err => { - // openSnack("Failed to save new preset"); - // }); + devicePresetAPI + .addDevicePreset(p.name, p.settings, objectKey, typestring) + .then(resp => { + //get the temp key from the preset + let temp = p.key; + //update the key in the preset with the key that came back from the add + p.key = resp.data.key; + //use the callback function passing in the preset and the temp key + if (addCallback) { + addCallback(p, temp); + } + openSnack("New preset saved"); + }) + .catch(err => { + openSnack("Failed to save new preset"); + }); }; const updatePreset = () => { @@ -108,32 +108,32 @@ export default function DevicePresetCard(props: Props) { newPreset.settings.temperature = tempC; newPreset.settings.humidity = hum; - // devicePresetAPI - // .updateDevicePreset(newPreset.key, newPreset.name, newPreset.settings) - // .then(resp => { - // if (updateCallback) { - // updateCallback(oldPreset, newPreset); - // } - // openSnack("Preset Updated"); - // }) - // .catch(err => { - // openSnack("There was a problem updating the preset"); - // }); + devicePresetAPI + .updateDevicePreset(newPreset.key, newPreset.name, newPreset.settings) + .then(resp => { + if (updateCallback) { + updateCallback(oldPreset, newPreset); + } + openSnack("Preset Updated"); + }) + .catch(err => { + openSnack("There was a problem updating the preset"); + }); }; const removePreset = () => { - // devicePresetAPI - // .removeDevicePreset(preset.key) - // .then(resp => { - // openSnack("Preset has been deleted"); - // setOpenDeleteConfirmation(false); - // if (removeCallback) { - // removeCallback(preset); - // } - // }) - // .catch(err => { - // openSnack("There was a problem deleting this preset"); - // }); + devicePresetAPI + .removeDevicePreset(preset.key) + .then(resp => { + openSnack("Preset has been deleted"); + setOpenDeleteConfirmation(false); + if (removeCallback) { + removeCallback(preset); + } + }) + .catch(err => { + openSnack("There was a problem deleting this preset"); + }); }; const deleteConfirmation = () => { @@ -192,7 +192,7 @@ export default function DevicePresetCard(props: Props) { {deleteConfirmation()} - + - + {presetType !== pond.PresetType.PRESET_TYPE_UNKNOWN && controllerType !== pond.ControllerType.CONTROLLER_TYPE_UNKNOWN && ( @@ -216,7 +216,7 @@ export default function DevicePresetCard(props: Props) { )} - + {/* type */} - + {/* controller type */} {/* temp slider */} {isMobile ? ( - + ) : ( - + Temperature - + - + + ) : ( - + Humidity - + - + Promise>; + //list + listDevicePresets: ( + limit: number, + offset: number, + order?: "asc" | "desc", + orderBy?: string, + search?: string, + keys?: string[], + types?: string[] + ) => Promise>; + //update + updateDevicePreset: ( + key: string, + name: string, + settings: pond.DevicePresetSettings + ) => Promise>; + + //remove + removeDevicePreset: (key: string) => Promise>; + + //get + getDevicePreset: (key: string) => Promise>; +} + +export const DevicePresetAPIcontext = createContext( + {} as IDevicePresetInterface +); + +interface Props {} + +export default function DevicePresetProvider(props: PropsWithChildren) { + const { children } = props; + const { get, del, post, put } = useHTTP(); + const [{ as }] = useGlobalState(); + + //add + const addDevicePreset = ( + name: string, + settings: pond.DevicePresetSettings, + parentKey?: string, + parentType?: string + ) => { + let key = ""; + let type = ""; + if (parentKey && parentType) { + key = "&parentKey=" + parentKey; + type = "&parentType=" + parentType; + } + if (as) { + return post( + pondURL("/devicePreset?name=" + name + "&as=" + as + key + type), + settings + ); + } + return post( + pondURL("/devicePreset?name=" + name + key + type), + settings + ); + }; + //list + const listDevicePresets = ( + limit: number, + offset: number, + order?: "asc" | "desc", + orderBy?: string, + search?: string, + keys?: string[], + types?: string[] + ) => { + let asText = ""; + if (as) asText = "&as=" + as; + return get( + pondURL( + "/devicePresets?limit=" + + limit + + "&offset=" + + offset + + ("&order=" + (order ? order : "asc")) + + ("&by=" + (orderBy ? orderBy : "key")) + + (search ? "&search=" + search : "") + + (keys ? "&keys=" + keys.toString() : "") + + (types ? "&types=" + types.toString() : "") + + asText + ) + ); + }; + //update + const updateDevicePreset = (key: string, name: string, settings: pond.DevicePresetSettings) => { + if (as) { + return put( + pondURL("/devicePreset/" + key + "?as=" + as + "&name=" + name), + settings + ); + } + return put( + pondURL("/devicePreset/" + key + "?name=" + name), + settings + ); + }; + + //remove + const removeDevicePreset = (key: string) => { + if (as) { + return del(pondURL("/devicePreset/" + key + "?as=" + as)); + } + return del(pondURL("/devicePreset/" + key + "?as=" + as)); + }; + + //get + const getDevicePreset = (key: string) => { + if (as) { + return del(pondURL("/devicePreset/" + key + "?as=" + as)); + } + return del(pondURL("/devicePreset/" + key + "?as=" + as)); + }; + return ( + + {children} + + ); +} + +export const useDevicePresetAPI = () => useContext(DevicePresetAPIcontext); diff --git a/src/providers/pond/pond.tsx b/src/providers/pond/pond.tsx index 8e879f9..6402ae4 100644 --- a/src/providers/pond/pond.tsx +++ b/src/providers/pond/pond.tsx @@ -36,6 +36,7 @@ import UsageProvider, { useUsageAPI } from "./usageAPI"; import KeyManagerProvider, { useKeyManagerAPI } from "./keyManagerAPI"; import JohnDeereProvider, { useJohnDeereProxyAPI } from "./johnDeereProxyAPI"; import CNHiProvider, { useCNHiProxyAPI } from "./cnhiProxyAPI"; +import DevicePresetProvider, { useDevicePresetAPI } from "./devicePresetAPI"; // import NoteProvider from "providers/noteAPI"; export const pondURL = (partial: string, demo: boolean = false): string => { @@ -88,7 +89,9 @@ export default function PondProvider(props: PropsWithChildren) { - {children} + + {children} + @@ -163,5 +166,6 @@ export { useUsageAPI, useKeyManagerAPI, useJohnDeereProxyAPI, - useCNHiProxyAPI + useCNHiProxyAPI, + useDevicePresetAPI }; From 5d9f4787a5874136e427081383b2283f609d63a8 Mon Sep 17 00:00:00 2001 From: Noor Date: Wed, 6 Aug 2025 09:29:38 -0600 Subject: [PATCH 5/8] minor update --- src/pages/Users.tsx | 3 ++- src/providers/index.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pages/Users.tsx b/src/pages/Users.tsx index 296c60a..45dd476 100644 --- a/src/pages/Users.tsx +++ b/src/pages/Users.tsx @@ -128,7 +128,8 @@ export default function Users() { "developer", "marketplace", "installer", - "cnhi" + "cnhi", + "libracart" ].sort(); const [rows, setRows] = useState([]) diff --git a/src/providers/index.ts b/src/providers/index.ts index 9689df0..cede030 100644 --- a/src/providers/index.ts +++ b/src/providers/index.ts @@ -45,7 +45,8 @@ export { useFileControllerAPI, useContractAPI, //TODO: update api with resolve, reject useJohnDeereProxyAPI, //TODO: update api with resolve, reject - useCNHiProxyAPI //TODO: update api with resolve, reject + useCNHiProxyAPI, //TODO: update api with resolve, reject + useLibraCartProxyAPI //TODO: update api with resolve, reject } from "./pond/pond"; // export { SecurityContext, useSecurity } from "./security"; export { SnackbarContext, useSnackbar } from "./Snackbar"; From a4819bdfdfbe818b9a1d2882c76f6e9e44d0dbca Mon Sep 17 00:00:00 2001 From: Saiyida Noor Date: Wed, 6 Aug 2025 17:05:32 +0000 Subject: [PATCH 6/8] minor updates --- .../LibraCart/LibraCartAccess.tsx | 3 +- src/pages/LibraCart.tsx | 80 +++++++++---------- 2 files changed, 41 insertions(+), 42 deletions(-) diff --git a/src/integrations/LibraCart/LibraCartAccess.tsx b/src/integrations/LibraCart/LibraCartAccess.tsx index 391a3fa..cfeb0f3 100644 --- a/src/integrations/LibraCart/LibraCartAccess.tsx +++ b/src/integrations/LibraCart/LibraCartAccess.tsx @@ -184,8 +184,7 @@ export default function LibraCartAccess() { Link Libra Cart Account To integrate with Libra Cart, navigate to Integrations page on Libra Cart and "Connect" to - Adaptive Agriculture App: https://staging.cloud.agrimatics.com/grain/integrations - {newOrgDialog()} + Adaptive Agriculture App. ); } diff --git a/src/pages/LibraCart.tsx b/src/pages/LibraCart.tsx index 5fc06eb..90f47b0 100644 --- a/src/pages/LibraCart.tsx +++ b/src/pages/LibraCart.tsx @@ -32,9 +32,9 @@ export default function LibraCart() { libracartAPI .listAccounts(0, 0, as) .then(resp => { - let tempOrgs: Map = new Map(); + let tempOrgs: Map = new Map(); resp.data.accounts.forEach(org => { - let organization = pond.JDAccount.fromObject(org); + let organization = pond.LibraCartAccount.fromObject(org); tempOrgs.set(organization.key, organization); }); setOrganizations(tempOrgs); @@ -70,40 +70,40 @@ export default function LibraCart() { }); }; - const fieldOptions = () => { - return ( - - { - setFieldAccordion(expanded); - }}> - }>Field Data - - - - { - //setFields(!fields); - updateOrgData(checked, pond.DataOption.DATA_OPTION_FIELDS); - }} - /> - } - /> - - - View your data from Libra Cart on your visual Farm - - - - - - ); - }; + // const fieldOptions = () => { + // return ( + // + // { + // setFieldAccordion(expanded); + // }}> + // }>Field Data + // + // + // + // { + // //setFields(!fields); + // updateOrgData(checked, pond.DataOption.DATA_OPTION_FIELDS); + // }} + // /> + // } + // /> + // + // + // View your data from Libra Cart + // + // + // + // + // + // ); + // }; return ( @@ -117,7 +117,7 @@ export default function LibraCart() { Promise>; - listFields: ( + listDestinations: ( limit: number, offset: number, - // order?: "asc" | "desc", - // orderBy?: string, - // search?: string, - as?: string, - asRoot?: boolean - ) => Promise>; + libracartKey?: string, + as?: string + ) => Promise>; } export const LibraCartProxyAPIContext = createContext( @@ -91,28 +88,6 @@ export default function LibraCartProvider(props: PropsWithChildren) { ); }; - const listFields = ( - limit: number, - offset: number, - // order?: "asc" | "desc", - // orderBy?: string, - // search?: string, - as?: string, - asRoot?: boolean - ) => { - return get( - pondURL( - "/libracartFields" + - "?limit=" + - limit + - "&offset=" + - offset + - (as ? "&as=" + as : "") + - (asRoot ? "&asRoot=" + asRoot.toString() : "") - ) - ); - }; - const updateAccount = (key: string, options: pond.DataOption[], as?: string) => { return put( pondURL( @@ -121,13 +96,21 @@ export default function LibraCartProvider(props: PropsWithChildren) { ); }; + const listDestinations = (limit: number, offset: number, libracartKey?: string, as?: string) => { + return get( + pondURL( + "/libracartDestinations?limit" + limit + "&offset=" + offset + (libracartKey ? "&libracartKey=" + libracartKey : "") + (as ? "&as=" + as : "") + ) + ) + } + return ( {children} From 714a2211549985518cfe1b61536f27878bdeca78 Mon Sep 17 00:00:00 2001 From: csawatzky Date: Wed, 13 Aug 2025 10:18:56 -0600 Subject: [PATCH 8/8] removed some logs and added to the logic in fillPercent in the bin model --- src/models/Bin.ts | 4 ++-- src/pages/LibraCart.tsx | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/models/Bin.ts b/src/models/Bin.ts index 1b855a8..603a9ce 100644 --- a/src/models/Bin.ts +++ b/src/models/Bin.ts @@ -126,7 +126,6 @@ export class Bin { } public bushels(): number { - console.log(this) let control = this.settings.inventory?.inventoryControl; let bushels = this.settings.inventory?.grainBushels || 0 if (control === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_AUTOMATIC_LIDAR || @@ -140,7 +139,8 @@ export class Bin { let fill = 0; if (this.settings.inventory && this.settings.specs) { let bushels = this.settings.inventory.grainBushels - if (this.settings.inventory.inventoryControl === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_AUTOMATIC_LIDAR){ + if (this.settings.inventory.inventoryControl === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_AUTOMATIC_LIDAR || + this.settings.inventory.inventoryControl === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_LIBRACART){ bushels = this.status.grainBushels } fill = Math.round( diff --git a/src/pages/LibraCart.tsx b/src/pages/LibraCart.tsx index cbb006f..4e93012 100644 --- a/src/pages/LibraCart.tsx +++ b/src/pages/LibraCart.tsx @@ -28,7 +28,6 @@ export default function LibraCart() { .listAccounts(0, 0, as) .then(resp => { let tempOrgs: Map = new Map(); - console.log(resp) resp.data.accounts.forEach(org => { let organization = pond.LibraCartAccount.fromObject(org); tempOrgs.set(organization.key, organization);