added the marketplace page to be able to add the jd and cnh features
This commit is contained in:
parent
2816884771
commit
411ff5b14c
5 changed files with 336 additions and 0 deletions
|
|
@ -21,6 +21,7 @@ import Ventilation from "pages/VentEditor";
|
||||||
// import Transactions from "pages/Transactions";
|
// import Transactions from "pages/Transactions";
|
||||||
// import BinCableEstimator from "pages/BinCableEstimator";
|
// import BinCableEstimator from "pages/BinCableEstimator";
|
||||||
import { useGlobalState } from "providers";
|
import { useGlobalState } from "providers";
|
||||||
|
import Marketplace from "userFeatures/UserFeatures";
|
||||||
// import Fields from "pages/Fields";
|
// import Fields from "pages/Fields";
|
||||||
//import Site from "pages/Site";
|
//import Site from "pages/Site";
|
||||||
|
|
||||||
|
|
@ -329,6 +330,7 @@ export default function Router(props: Props) {
|
||||||
<Route path="api" element={<APIDocs />} />
|
<Route path="api" element={<APIDocs />} />
|
||||||
}
|
}
|
||||||
<Route path="fields" element={<Fields />} />
|
<Route path="fields" element={<Fields />} />
|
||||||
|
<Route path="marketplace" element={<Marketplace />} />
|
||||||
|
|
||||||
{/* Map pages */}
|
{/* Map pages */}
|
||||||
<Route path="visualFarm" element={<FieldMap />} />
|
<Route path="visualFarm" element={<FieldMap />} />
|
||||||
|
|
|
||||||
|
|
@ -366,6 +366,18 @@ export default function SideNavigator(props: Props) {
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
|
<Tooltip title="Marketplace" placement="right">
|
||||||
|
<ListItemButton
|
||||||
|
id="tour-marketplace"
|
||||||
|
onClick={() => goTo("/marketplace")}
|
||||||
|
classes={getClasses("/marketplace")}
|
||||||
|
>
|
||||||
|
<ListItemIcon>
|
||||||
|
<Code />
|
||||||
|
</ListItemIcon>
|
||||||
|
{open && <ListItemText primary="Marketplace" />}
|
||||||
|
</ListItemButton>
|
||||||
|
</Tooltip>
|
||||||
</List>
|
</List>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
10
src/pages/Marketplace.tsx
Normal file
10
src/pages/Marketplace.tsx
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import PageContainer from "./PageContainer";
|
||||||
|
import UserFeatures from "userFeatures/UserFeatures";
|
||||||
|
|
||||||
|
export default function Marketplace() {
|
||||||
|
return (
|
||||||
|
<PageContainer>
|
||||||
|
<UserFeatures />
|
||||||
|
</PageContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
169
src/userFeatures/FeatureCard.tsx
Normal file
169
src/userFeatures/FeatureCard.tsx
Normal file
|
|
@ -0,0 +1,169 @@
|
||||||
|
import {
|
||||||
|
// Accordion,
|
||||||
|
// AccordionDetails,
|
||||||
|
// AccordionSummary,
|
||||||
|
Avatar,
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
DialogActions,
|
||||||
|
DialogContent,
|
||||||
|
Divider,
|
||||||
|
Grid2 as Grid,
|
||||||
|
List,
|
||||||
|
ListItem,
|
||||||
|
Typography
|
||||||
|
} from "@mui/material";
|
||||||
|
// import { Close, ExpandMore } from "@material-ui/icons";
|
||||||
|
import { Close } from "@mui/icons-material";
|
||||||
|
import ResponsiveDialog from "common/ResponsiveDialog";
|
||||||
|
//import { useMobile } from "hooks";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { ProductDetails } from "./UserFeatures";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
feature: ProductDetails;
|
||||||
|
hasFeature: boolean;
|
||||||
|
updateFeature: (add: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function FeatureCard(props: Props) {
|
||||||
|
const { feature, hasFeature, updateFeature } = props;
|
||||||
|
const [openDialog, setOpenDialog] = useState(false);
|
||||||
|
// const [currentQuestion, setCurrentQuestion] = useState(0);
|
||||||
|
//const isMobile = useMobile()
|
||||||
|
|
||||||
|
// const handleExpand = (index: number) => {
|
||||||
|
// setCurrentQuestion(index === currentQuestion ? 0 : index);
|
||||||
|
// };
|
||||||
|
|
||||||
|
const productDialog = () => {
|
||||||
|
return (
|
||||||
|
<ResponsiveDialog
|
||||||
|
open={openDialog}
|
||||||
|
onClose={() => {
|
||||||
|
setOpenDialog(false);
|
||||||
|
}}>
|
||||||
|
<DialogActions style={{ padding: 0 }}>
|
||||||
|
<img width="100%" alt={feature.featureName} src={feature.cardImage} />
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setOpenDialog(false);
|
||||||
|
}}
|
||||||
|
style={{ position: "absolute", top: "10px" }}>
|
||||||
|
<Close />
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
<DialogContent style={{ padding: 8 }}>
|
||||||
|
<List>
|
||||||
|
<ListItem>
|
||||||
|
<Grid
|
||||||
|
container
|
||||||
|
direction="row"
|
||||||
|
alignItems="center"
|
||||||
|
spacing={2}
|
||||||
|
wrap="nowrap"
|
||||||
|
justifyContent="space-between">
|
||||||
|
<Grid>
|
||||||
|
<Avatar style={{ height: 40, width: 40 }}>{feature.featureLogo}</Avatar>
|
||||||
|
</Grid>
|
||||||
|
<Grid>
|
||||||
|
<Typography style={{ fontWeight: 650, fontSize: 15 }}>
|
||||||
|
{feature.featureTitle}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
onClick={() => {
|
||||||
|
updateFeature(!hasFeature);
|
||||||
|
}}>
|
||||||
|
{hasFeature ? "Deactivate" : "Activate"}
|
||||||
|
</Button>
|
||||||
|
<Typography style={{ fontWeight: 350, fontSize: 15 }}>
|
||||||
|
{feature.featureCost > 0 ? feature.featureCost : "Free"}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</ListItem>
|
||||||
|
{/* <Divider />
|
||||||
|
<ListItem>
|
||||||
|
Possibly put Screenshots Here
|
||||||
|
</ListItem> */}
|
||||||
|
<Divider />
|
||||||
|
<ListItem>
|
||||||
|
<Box>
|
||||||
|
<Typography style={{ fontWeight: 350, fontSize: 13 }}>
|
||||||
|
{feature.longDescription}
|
||||||
|
</Typography>
|
||||||
|
{/* {feature.bulletPoints && (
|
||||||
|
<ul>
|
||||||
|
{feature.bulletPoints.map((b, i) => (
|
||||||
|
<li key={i} style={{ display: "list-item" }}>
|
||||||
|
<Typography style={{ fontWeight: 350, fontSize: 13 }}>{b}</Typography>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)} */}
|
||||||
|
</Box>
|
||||||
|
</ListItem>
|
||||||
|
<Divider />
|
||||||
|
<ListItem disableGutters>
|
||||||
|
<Box style={{ width: "100%" }}>
|
||||||
|
{/* {feature.questions.map((q, i) => (
|
||||||
|
<Accordion
|
||||||
|
key={i}
|
||||||
|
expanded={currentQuestion === i + 1}
|
||||||
|
onChange={() => handleExpand(i + 1)}>
|
||||||
|
<AccordionSummary expandIcon={<ExpandMore />}>
|
||||||
|
<Typography style={{ fontWeight: 650, fontSize: 13 }}>
|
||||||
|
{q.question}
|
||||||
|
</Typography>
|
||||||
|
</AccordionSummary>
|
||||||
|
<AccordionDetails>
|
||||||
|
<Typography style={{ fontWeight: 350, fontSize: 13 }}>{q.answer}</Typography>
|
||||||
|
</AccordionDetails>
|
||||||
|
</Accordion>
|
||||||
|
))} */}
|
||||||
|
</Box>
|
||||||
|
</ListItem>
|
||||||
|
</List>
|
||||||
|
</DialogContent>
|
||||||
|
</ResponsiveDialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{productDialog()}
|
||||||
|
<Card
|
||||||
|
style={{ height: "100%", cursor: "pointer" }}
|
||||||
|
onClick={() => {
|
||||||
|
setOpenDialog(true);
|
||||||
|
}}>
|
||||||
|
<Box width="100%" alignItems="center" display="flex" justifyContent="center">
|
||||||
|
<img width="100%" alt={feature.featureName} src={feature.cardImage} />
|
||||||
|
</Box>
|
||||||
|
<Grid
|
||||||
|
style={{ padding: 10 }}
|
||||||
|
container
|
||||||
|
direction="row"
|
||||||
|
alignItems="center"
|
||||||
|
spacing={1}
|
||||||
|
wrap="nowrap">
|
||||||
|
<Grid>
|
||||||
|
<Avatar style={{ height: 40, width: 40 }}>{feature.featureLogo}</Avatar>
|
||||||
|
</Grid>
|
||||||
|
<Grid>
|
||||||
|
<Typography style={{ fontWeight: 650, fontSize: 17 }}>
|
||||||
|
{feature.featureTitle}
|
||||||
|
</Typography>
|
||||||
|
<Typography style={{ fontWeight: 350, fontSize: 15 }}>
|
||||||
|
{feature.featureCost > 0 ? feature.featureCost : "Free"}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Card>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
143
src/userFeatures/UserFeatures.tsx
Normal file
143
src/userFeatures/UserFeatures.tsx
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
import { Box, Grid2 as Grid } from "@mui/material";
|
||||||
|
|
||||||
|
import { useGlobalState } from "providers";
|
||||||
|
import { useSnackbar, useUserAPI } from "hooks";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import JohnDeereIcon from "products/CommonIcons/johnDeereIcon";
|
||||||
|
import CNHiIcon from "products/CommonIcons/cnhiIcon";
|
||||||
|
//import AgLogo from "assets/whitelabels/AdaptiveAgriculture/AGLogoSquare.png";
|
||||||
|
import {
|
||||||
|
IsAdaptiveAgriculture
|
||||||
|
// IsAdCon,
|
||||||
|
// IsMiVent,
|
||||||
|
// IsOmniAir
|
||||||
|
} from "services/whiteLabel";
|
||||||
|
import FeatureCard from "./FeatureCard";
|
||||||
|
//import { ImgIcon } from "common/ImgIcon";
|
||||||
|
//images to import for the image in the card for the feature make sure to have a 2:1 aspect ratio to keep the cards symmetrical
|
||||||
|
import FeatureImageTest from "assets/marketplaceImages/VisualFarmMPImage.png";
|
||||||
|
|
||||||
|
// export interface FAQ {
|
||||||
|
// question: string;
|
||||||
|
// answer: string;
|
||||||
|
// }
|
||||||
|
|
||||||
|
export interface ProductDetails {
|
||||||
|
featureName: string; //string matching the entry in the features array found in Users.tsx
|
||||||
|
featureTitle: string; //title of feature to display to user
|
||||||
|
featureCost: number;
|
||||||
|
cardImage: string; //image to display on the feature card
|
||||||
|
featureLogo: JSX.Element; //image to display in the logo section of the card and product dialog
|
||||||
|
//galleryImages: string[] //screenshots to show the feature in action
|
||||||
|
longDescription: string; //a longer description of the feature to show in the dialog
|
||||||
|
// bulletPoints?: string[];
|
||||||
|
// questions: FAQ[];
|
||||||
|
hidden?: boolean; //if the feature is not quite ready for the public ie in testing, if a user has the beta flag they will see these hidden features
|
||||||
|
}
|
||||||
|
|
||||||
|
const agFeatureList: ProductDetails[] = [
|
||||||
|
{
|
||||||
|
featureName: "john-deere",
|
||||||
|
featureLogo: <JohnDeereIcon />,
|
||||||
|
featureCost: 0,
|
||||||
|
cardImage: FeatureImageTest,
|
||||||
|
featureTitle: "John Deere Operations Center",
|
||||||
|
longDescription:
|
||||||
|
"Integrate with the John Deere Operations Center to bring your data into our platform"
|
||||||
|
// bulletPoints: ["bullet one", "bullet two"],
|
||||||
|
// questions: [
|
||||||
|
// { question: "question 1", answer: "answer 1" },
|
||||||
|
// { question: "question 2", answer: "answer 2" }
|
||||||
|
// ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
featureName: "cnhi",
|
||||||
|
featureLogo: <CNHiIcon />,
|
||||||
|
featureCost: 0,
|
||||||
|
cardImage: FeatureImageTest,
|
||||||
|
featureTitle: "Case New Holland Industrial Center",
|
||||||
|
longDescription:
|
||||||
|
"Integrate with the Case New Holland Industrial Center to bring your data into our platform"
|
||||||
|
// bulletPoints: ["bullet one", "bullet two"],
|
||||||
|
// questions: [],
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// const constructionFeatureList: ProductDetails[] = []
|
||||||
|
|
||||||
|
// const aviationFeatureList: ProductDetails[] = []
|
||||||
|
|
||||||
|
// const miningFeatureList: ProductDetails[] = []
|
||||||
|
|
||||||
|
// const universalFeatureList: ProductDetails[] = []
|
||||||
|
|
||||||
|
export default function Marketplace() {
|
||||||
|
const [{ user }, dispatch] = useGlobalState();
|
||||||
|
const userAPI = useUserAPI();
|
||||||
|
const [userFeatures, setUserFeatures] = useState<string[]>([]);
|
||||||
|
const [featureList, setFeaturList] = useState<ProductDetails[]>([]);
|
||||||
|
const { error, success } = useSnackbar();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let list: ProductDetails[] = [];
|
||||||
|
if (IsAdaptiveAgriculture()) {
|
||||||
|
list = list.concat(agFeatureList);
|
||||||
|
}
|
||||||
|
// if(IsAdCon()){
|
||||||
|
// list = list.concat(constructionFeatureList)
|
||||||
|
// }
|
||||||
|
// if(IsOmniAir()){
|
||||||
|
// list = list.concat(aviationFeatureList)
|
||||||
|
// }
|
||||||
|
// if(IsMiVent()){
|
||||||
|
// list = list.concat(miningFeatureList)
|
||||||
|
// }
|
||||||
|
// list = list.concat(universalFeatureList)
|
||||||
|
setFeaturList(list);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setUserFeatures(user.settings.features);
|
||||||
|
}, [user]);
|
||||||
|
|
||||||
|
const updateUser = (feature: string, added: boolean) => {
|
||||||
|
if (added) {
|
||||||
|
user.settings.features.push(feature);
|
||||||
|
} else {
|
||||||
|
user.settings.features = user.settings.features.filter(v => v !== feature);
|
||||||
|
}
|
||||||
|
setUserFeatures([...user.settings.features]);
|
||||||
|
userAPI
|
||||||
|
.updateUser(user.id(), user.protobuf())
|
||||||
|
.then(resp => {
|
||||||
|
success("Updated " + feature + " feature");
|
||||||
|
dispatch({ key: "user", value: user });
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
error("Failed to update " + feature + " feature");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box padding={2}>
|
||||||
|
<Grid container spacing={3} justifyContent="space-between" alignItems="center" alignContent="center">
|
||||||
|
{featureList.map(feat => {
|
||||||
|
if (!feat.hidden || user.hasFeature("beta")) {
|
||||||
|
return (
|
||||||
|
<Grid key={feat.featureName} size={{ xs:12, sm:6, md:4, lg:4}}>
|
||||||
|
<FeatureCard
|
||||||
|
feature={feat}
|
||||||
|
updateFeature={checked => {
|
||||||
|
updateUser(feat.featureName, checked);
|
||||||
|
}}
|
||||||
|
hasFeature={userFeatures.includes(feat.featureName)}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})}
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue