added a welcome tour to the signupCallback page
This commit is contained in:
parent
c9bf0d11ad
commit
4f8b0254bb
10 changed files with 247 additions and 32 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import Joyride, { Step, CallBackProps, STATUS, TooltipRenderProps, Locale } from "react-joyride";
|
||||
import Joyride, { Step, CallBackProps, STATUS, TooltipRenderProps, Locale, ACTIONS, EVENTS } from "react-joyride";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
|
|
@ -10,12 +10,13 @@ import {
|
|||
Tooltip,
|
||||
IconButton,
|
||||
Theme,
|
||||
useTheme
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import { KeyboardArrowLeft, KeyboardArrowRight, Close as CloseIcon } from "@mui/icons-material";
|
||||
import classNames from "classnames";
|
||||
import { grey } from "@mui/material/colors";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { CSSProperties } from "react";
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) => {
|
||||
return ({
|
||||
|
|
@ -46,6 +47,10 @@ const useStyles = makeStyles((theme: Theme) => {
|
|||
})
|
||||
});
|
||||
|
||||
export interface TourStep extends Step {
|
||||
onNext?: () => void; // Define onNext as an optional function
|
||||
}
|
||||
|
||||
const StepContainer = (props: TooltipRenderProps, numSteps: number, skip: () => void) => {
|
||||
const {
|
||||
continuous,
|
||||
|
|
@ -114,18 +119,30 @@ const StepContainer = (props: TooltipRenderProps, numSteps: number, skip: () =>
|
|||
|
||||
interface Props {
|
||||
run: boolean;
|
||||
steps: Step[];
|
||||
steps: TourStep[];
|
||||
endTourCallback: () => void;
|
||||
spotlightStyle?: CSSProperties,
|
||||
setStepIndex?: React.Dispatch<React.SetStateAction<number>>,
|
||||
}
|
||||
|
||||
export default function Tour(props: Props) {
|
||||
const { run, steps, endTourCallback } = props;
|
||||
const { run, steps, endTourCallback, setStepIndex, spotlightStyle } = props;
|
||||
const theme = useTheme();
|
||||
|
||||
const handleTourCallback = (data: CallBackProps) => {
|
||||
const { status } = data;
|
||||
const { action, index, status, type } = data;
|
||||
const finishedStatuses: string[] = [STATUS.FINISHED, STATUS.SKIPPED];
|
||||
|
||||
if (setStepIndex) setStepIndex(index)
|
||||
|
||||
if (action === ACTIONS.NEXT && type === EVENTS.STEP_AFTER) {
|
||||
// Safely access the step and call onNext if it exists
|
||||
const currentStep = steps[index];
|
||||
if (currentStep.onNext) {
|
||||
currentStep.onNext();
|
||||
}
|
||||
}
|
||||
|
||||
if (finishedStatuses.includes(status)) {
|
||||
endTourCallback();
|
||||
}
|
||||
|
|
@ -150,6 +167,9 @@ export default function Tour(props: Props) {
|
|||
primaryColor: theme.palette.primary.main,
|
||||
textColor: theme.palette.text.primary,
|
||||
zIndex: theme.zIndex.modal
|
||||
},
|
||||
spotlight: {
|
||||
...spotlightStyle
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ export default function SideNavigator(props: Props) {
|
|||
{(isAg || isStreamline || user.hasFeature("admin")) && (
|
||||
<Tooltip title="Bins" placement="right">
|
||||
<ListItemButton
|
||||
id="tour-dashboard"
|
||||
id="tour-bins"
|
||||
onClick={() => goTo("/bins")}
|
||||
classes={getClasses("/bin")}
|
||||
>
|
||||
|
|
@ -277,7 +277,7 @@ export default function SideNavigator(props: Props) {
|
|||
{(isMiVent || user.hasFeature("admin")) && (
|
||||
<Tooltip title="Mines" placement="right">
|
||||
<ListItemButton
|
||||
id="tour-dashboard"
|
||||
id="tour-mines"
|
||||
onClick={() => goTo("/mines")}
|
||||
classes={getClasses("/mine")}
|
||||
>
|
||||
|
|
@ -330,7 +330,7 @@ export default function SideNavigator(props: Props) {
|
|||
}
|
||||
<Tooltip title="Teams" placement="right">
|
||||
<ListItemButton
|
||||
id="tour-dashboard"
|
||||
id="tour-teams"
|
||||
onClick={() => goTo("/teams")}
|
||||
classes={getClasses("/team")}
|
||||
>
|
||||
|
|
@ -342,7 +342,7 @@ export default function SideNavigator(props: Props) {
|
|||
</Tooltip>
|
||||
<Tooltip title="Users" placement="right">
|
||||
<ListItemButton
|
||||
id="tour-dashboard"
|
||||
id="tour-users"
|
||||
onClick={() => goTo("/users")}
|
||||
classes={getClasses("/user")}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import {
|
|||
TypeBackground
|
||||
} from "@mui/material";
|
||||
import { blue, green, orange, red } from "@mui/material/colors";
|
||||
import { createStyles, makeStyles } from "@mui/styles";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
//import { TypeBackground } from "@material-ui/core/styles/createPalette";
|
||||
import {
|
||||
ArrowUpward,
|
||||
|
|
@ -267,7 +267,7 @@ export default function Firmware() {
|
|||
if (selected) {
|
||||
firmwareAPI
|
||||
.removeFirmware(selected.settings.platform, selected.settings.version)
|
||||
.then((response: any) => {
|
||||
.then(() => {
|
||||
success("Successfully removed firmware");
|
||||
})
|
||||
.catch((err: any) => {
|
||||
|
|
|
|||
|
|
@ -1,26 +1,36 @@
|
|||
import { useDeviceAPI, useMobile, useUserAPI } from "hooks";
|
||||
import PageContainer from "./PageContainer";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Button, CircularProgress, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Divider } from "@mui/material";
|
||||
import { useBinAPI, useTeamAPI } from "providers";
|
||||
import { Button, CircularProgress, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Divider, Typography } from "@mui/material";
|
||||
import { useBinAPI, useGlobalState, useTeamAPI } from "providers";
|
||||
import { CheckCircleOutline } from "@mui/icons-material";
|
||||
import { green } from "@mui/material/colors";
|
||||
import Tour, { TourStep } from "common/Tour";
|
||||
import Emoji from "react-emoji-render";
|
||||
import { getWhitelabel, IsAdaptiveAgriculture } from "services/whiteLabel";
|
||||
// import { color } from "framer-motion";
|
||||
|
||||
interface Props {
|
||||
// interface Props {
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
export default function SignupCallback (props: Props) {
|
||||
// export default function SignupCallback (props: Props) {
|
||||
export default function SignupCallback () {
|
||||
const userAPI = useUserAPI();
|
||||
const teamAPI = useTeamAPI()
|
||||
const deviceAPI = useDeviceAPI();
|
||||
const binAPI = useBinAPI();
|
||||
const isMobile = useMobile()
|
||||
const [{ user }] = useGlobalState();
|
||||
const whiteLabel = getWhitelabel()
|
||||
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [signupDialogOpen, setSignupDialogOpen] = useState(true)
|
||||
const [hasTeams, setHasTeams] = useState(false)
|
||||
const [hasDevices, setHasDevices] = useState(false)
|
||||
const [hasBins, setHasBins] = useState(false)
|
||||
const [isTourRunning, setIsTourRunning] = useState(false);
|
||||
const [stepIndex, setStepIndex] = useState(0)
|
||||
|
||||
const [doneBins, setDoneBins] = useState(false)
|
||||
const [doneTeams, setDoneTeams] = useState(false)
|
||||
|
|
@ -28,7 +38,7 @@ export default function SignupCallback (props: Props) {
|
|||
|
||||
useEffect(() => {
|
||||
setLoading(true)
|
||||
userAPI.completeSignup().then(resp => {
|
||||
userAPI.completeSignup().then(() => {
|
||||
teamAPI.listTeams(1, 0).then(resp => {
|
||||
setHasTeams(resp.data.teams.length > 0)
|
||||
}).finally(() => setDoneTeams(true))
|
||||
|
|
@ -55,13 +65,131 @@ export default function SignupCallback (props: Props) {
|
|||
}
|
||||
<DialogContentText sx={{marginTop: 2}}>
|
||||
{hasTeams && "Teams found!"}
|
||||
<br/>
|
||||
{hasDevices && "Devices found!"}
|
||||
<br/>
|
||||
{hasBins && "Bins found!"}
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
)
|
||||
}
|
||||
|
||||
const getTourSteps = (): TourStep[] => {
|
||||
let steps: TourStep[] = [
|
||||
{
|
||||
title: (
|
||||
<>
|
||||
Welcome to {whiteLabel.name}
|
||||
<Emoji text=" 🎉" />
|
||||
</>
|
||||
),
|
||||
content: (
|
||||
<>
|
||||
<Typography variant="subtitle2" gutterBottom>
|
||||
{"Hello " + user.name()}
|
||||
<Emoji text=" 👋" />
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
Thank you for signing up! Let me give you the tour
|
||||
<Emoji text=" 😊" />
|
||||
</Typography>
|
||||
</>
|
||||
),
|
||||
target: "body",
|
||||
placement: "center",
|
||||
disableBeacon: true,
|
||||
},
|
||||
{
|
||||
title: "User Menu",
|
||||
content: (
|
||||
<>
|
||||
<Typography variant="body2" >
|
||||
In the user menu, you can make changes to your profile, and adjust unit preferences.
|
||||
</Typography>
|
||||
<br/>
|
||||
<Typography variant="body2" >
|
||||
If you are part of a team, you can select it here as well.
|
||||
</Typography>
|
||||
</>
|
||||
),
|
||||
target: "#tour-user-menu",
|
||||
placement: "bottom",
|
||||
disableBeacon: true,
|
||||
// onNext: () => setDoneFirst(true)
|
||||
},
|
||||
// {
|
||||
// title:
|
||||
// }
|
||||
];
|
||||
if (hasTeams) steps.push({
|
||||
title: "Teams",
|
||||
content: (
|
||||
<>
|
||||
<Typography variant="body2" >
|
||||
Here, you can view your team(s), access their settings, and choose your favourite.
|
||||
</Typography>
|
||||
</>
|
||||
),
|
||||
target: "#tour-teams",
|
||||
placement: "right",
|
||||
// spotlightPadding: 50,
|
||||
})
|
||||
steps.push({
|
||||
title: "Devices",
|
||||
content: (
|
||||
<>
|
||||
<Typography variant="body2" >
|
||||
You can view your devices and their readings here.
|
||||
</Typography>
|
||||
<br/>
|
||||
<Typography>
|
||||
If you are viewing as a team, your team's devices will be displayed here instead.
|
||||
</Typography>
|
||||
</>
|
||||
),
|
||||
target: "#tour-dashboard",
|
||||
placement: "right"
|
||||
})
|
||||
if (IsAdaptiveAgriculture()) steps.push({
|
||||
title: "Bins",
|
||||
content: (
|
||||
<>
|
||||
<Typography variant="body2" >
|
||||
You can view your bins and their inventory here.
|
||||
</Typography>
|
||||
<br/>
|
||||
<Typography>
|
||||
If you are viewing as a team, your team's bins will be displayed here instead.
|
||||
</Typography>
|
||||
</>
|
||||
),
|
||||
target: "#tour-bins",
|
||||
placement: "right",
|
||||
disableBeacon: true,
|
||||
})
|
||||
return steps;
|
||||
};
|
||||
|
||||
const endTour = () => {
|
||||
setIsTourRunning(false);
|
||||
// if (user) {
|
||||
// user.status.finishedBinIntro = moment().toJSON();
|
||||
// userAPI
|
||||
// .updateUser(userID, user.protobuf())
|
||||
// .then(() => dispatch({ key: "user", value: user }))
|
||||
// .catch((err: any) => error(err));
|
||||
// }
|
||||
};
|
||||
|
||||
const closeDialog = () => {
|
||||
setSignupDialogOpen(false)
|
||||
setIsTourRunning(true)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
console.log(stepIndex)
|
||||
}, [stepIndex])
|
||||
|
||||
return (
|
||||
<PageContainer spacing={1}>
|
||||
<Dialog open={signupDialogOpen} fullScreen={isMobile} >
|
||||
|
|
@ -72,12 +200,18 @@ export default function SignupCallback (props: Props) {
|
|||
{ content() }
|
||||
<DialogActions>
|
||||
{loadingComplete() &&
|
||||
<Button onClick={() => setSignupDialogOpen(false)}>
|
||||
<Button onClick={closeDialog}>
|
||||
Done
|
||||
</Button>
|
||||
}
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
<Tour
|
||||
setStepIndex={setStepIndex}
|
||||
spotlightStyle={{ marginTop: stepIndex!==1 ? 74 : 1 }}
|
||||
run={isTourRunning} steps={getTourSteps()}
|
||||
endTourCallback={endTour}
|
||||
/>
|
||||
</PageContainer>
|
||||
)
|
||||
}
|
||||
|
|
@ -349,7 +349,7 @@ export default function Users() {
|
|||
}
|
||||
|
||||
return (
|
||||
<PageContainer padding={isMobile ? 0 : 2}>
|
||||
<PageContainer spacing={isMobile ? 0 : 2}>
|
||||
<ResponsiveTable<User>
|
||||
title="Users"
|
||||
rows={rows}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,13 @@ interface Props extends PropsWithChildren<any>{
|
|||
|
||||
export default function AuthWrapper(props: Props) {
|
||||
const { children, setToken } = props;
|
||||
const { isLoading, loginWithRedirect, isAuthenticated, getAccessTokenSilently, getIdTokenClaims } = useAuth0();
|
||||
const {
|
||||
isLoading,
|
||||
loginWithRedirect,
|
||||
isAuthenticated,
|
||||
getAccessTokenSilently,
|
||||
// getIdTokenClaims
|
||||
} = useAuth0();
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -42,8 +48,8 @@ export default function AuthWrapper(props: Props) {
|
|||
}, [isAuthenticated, loginWithRedirect, isLoading]);
|
||||
|
||||
useEffect(() => {
|
||||
const url = new URL(window.location.href);
|
||||
const isSignupCallback = url.pathname.includes("/signupCallback");
|
||||
// const url = new URL(window.location.href);
|
||||
// const isSignupCallback = url.pathname.includes("/signupCallback");
|
||||
if (isAuthenticated) {
|
||||
getAccessTokenSilently().then(t => {
|
||||
if (t.length < 1) {
|
||||
|
|
@ -54,14 +60,13 @@ export default function AuthWrapper(props: Props) {
|
|||
// No token, go back to login
|
||||
loginWithRedirect()
|
||||
})
|
||||
if (isSignupCallback) {
|
||||
console.log("IS SIGNUP")
|
||||
getIdTokenClaims().then(id_claims => {
|
||||
console.log(id_claims)
|
||||
console.log(id_claims?.nickname)
|
||||
if (id_claims) console.log(Object.keys(id_claims))
|
||||
})
|
||||
}
|
||||
// if (isSignupCallback) {
|
||||
// getIdTokenClaims().then(id_claims => {
|
||||
// console.log(id_claims)
|
||||
// console.log(id_claims?.nickname)
|
||||
// if (id_claims) console.log(Object.keys(id_claims))
|
||||
// })
|
||||
// }
|
||||
|
||||
}
|
||||
}, [setToken, isAuthenticated])
|
||||
|
|
|
|||
|
|
@ -67,7 +67,6 @@ export default function UserProvider(props: PropsWithChildren<any>) {
|
|||
request.id = user.id()
|
||||
request.name = user.name()
|
||||
post(pondURL("/signups"), request).then(res => {
|
||||
console.log(res)
|
||||
resolve(pond.CompleteSignupUserResponse.create())
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@ export default function UserMenu() {
|
|||
<ListItemText primary="Toggle Theme" />
|
||||
</MenuItem> */}
|
||||
{hasTeams && (
|
||||
<MenuItem onClick={openTeamDialog} aria-label="Open Team Menu" dense>
|
||||
<MenuItem id="tour-teams" onClick={openTeamDialog} aria-label="Open Team Menu" dense>
|
||||
<ListItemIcon>
|
||||
<TeamIcon />
|
||||
</ListItemIcon>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue