frontend/src/pages/SignupCallback.tsx
2026-05-21 11:03:34 -06:00

413 lines
No EOL
13 KiB
TypeScript

import { useDeviceAPI, useMobile, useUserAPI } from "hooks";
import PageContainer from "./PageContainer";
import { useEffect, useState } from "react";
import { Button, CircularProgress, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Divider, Grid2, MenuItem, TextField, 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, getFeatures } from "services/whiteLabel";
import { pond } from "protobuf-ts/pond";
import { User } from "models";
// import { color } from "framer-motion";
// interface 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 [globalState, dispatch] = 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)
const [doneDevices, setDoneDevices] = useState(false)
const [user, setUser] = useState<User>(globalState.user);
useEffect(() => {
setLoading(true)
userAPI.completeSignup().then(() => {
teamAPI.listTeams(1, 0).then(resp => {
setHasTeams(resp.data.teams.length > 0)
}).finally(() => setDoneTeams(true))
deviceAPI.list(1, 0).then(resp => {
setHasDevices(resp.data.devices.length > 0)
}).finally(() => setDoneDevices(true))
binAPI.listBins(1, 0).then(resp => {
setHasBins(resp.data.bins.length > 0)
}).finally(() => setDoneBins(true))
}).finally(() => setLoading(false))
}, [])
const loadingComplete = () => {
return loading === false && doneBins && doneTeams && doneDevices
}
const changePressureUnit = (value: any) => {
let updatedUser = User.clone(user)
let pressureUnit: pond.PressureUnit;
switch (value) {
case 1:
pressureUnit = pond.PressureUnit.PRESSURE_UNIT_KILOPASCALS;
break;
case 2:
pressureUnit = pond.PressureUnit.PRESSURE_UNIT_INCHES_OF_WATER;
break;
default:
pressureUnit = pond.PressureUnit.PRESSURE_UNIT_UNKNOWN;
break;
}
updatedUser.settings.pressureUnit = pressureUnit;
setUser(updatedUser)
};
const changeTemperatureUnit = (value: any) => {
let updatedUser = User.clone(user);
let temperatureUnit: pond.TemperatureUnit;
switch (value) {
case 1:
temperatureUnit = pond.TemperatureUnit.TEMPERATURE_UNIT_CELSIUS;
break;
case 2:
temperatureUnit = pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT;
break;
default:
temperatureUnit = pond.TemperatureUnit.TEMPERATURE_UNIT_UNKNOWN;
break;
}
updatedUser.settings.temperatureUnit = temperatureUnit;
setUser(updatedUser);
};
const unitPreferences = () => {
const { pressureUnit, temperatureUnit, distanceUnit, grainUnit } = user.settings;
return (
<Grid2 container>
<Grid2 size={{ xs: 12 }}>
<TextField
select
fullWidth
id="pressureUnit"
name="pressureUnit"
label="Pressure Unit"
value={pressureUnit ? pressureUnit : pond.PressureUnit.PRESSURE_UNIT_INCHES_OF_WATER}
onChange={event => changePressureUnit(event.target.value)}
margin="normal"
variant="outlined"
InputLabelProps={{ shrink: true }}>
<MenuItem value={pond.PressureUnit.PRESSURE_UNIT_KILOPASCALS}>
Kilopascals (kPa)
</MenuItem>
<MenuItem value={pond.PressureUnit.PRESSURE_UNIT_INCHES_OF_WATER}>
Inches of Water (iwg)
</MenuItem>
</TextField>
</Grid2>
<Grid2 size={{ xs: 12 }}>
<TextField
select
fullWidth
id="temperatureUnit"
name="temperatureUnit"
label="Temperature Unit"
value={
temperatureUnit ? temperatureUnit : pond.TemperatureUnit.TEMPERATURE_UNIT_CELSIUS
}
onChange={event => changeTemperatureUnit(event.target.value)}
margin="normal"
variant="outlined"
InputLabelProps={{ shrink: true }}>
<MenuItem value={pond.TemperatureUnit.TEMPERATURE_UNIT_CELSIUS}>Celsius (°C)</MenuItem>
<MenuItem value={pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT}>
Fahrenheit (°F)
</MenuItem>
</TextField>
</Grid2>
<Grid2 size={{ xs: 12 }}>
<TextField
select
fullWidth
id="distanceUnit"
name="distanceUnit"
label="Distance Unit"
value={distanceUnit ? distanceUnit : pond.DistanceUnit.DISTANCE_UNIT_METERS}
// onChange={event => changeDistanceUnit(event.target.value)}
margin="normal"
variant="outlined"
InputLabelProps={{ shrink: true }}>
<MenuItem value={pond.DistanceUnit.DISTANCE_UNIT_METERS}>Meters (m)</MenuItem>
<MenuItem value={pond.DistanceUnit.DISTANCE_UNIT_FEET}>Feet (ft)</MenuItem>
</TextField>
</Grid2>
{getFeatures().grainUnit && (
<Grid2 size={{ xs: 12 }}>
<TextField
select
fullWidth
id="grainUnit"
name="grainUnit"
label="Grain Unit"
value={grainUnit ? grainUnit : pond.GrainUnit.GRAIN_UNIT_BUSHELS}
// onChange={event => changeGrainUnit(event.target.value)}
margin="normal"
variant="outlined"
InputLabelProps={{ shrink: true }}>
<MenuItem value={pond.GrainUnit.GRAIN_UNIT_BUSHELS}>Bushels (bu)</MenuItem>
<MenuItem value={pond.GrainUnit.GRAIN_UNIT_TONNE}>Tonnes (mT)</MenuItem>
<MenuItem value={pond.GrainUnit.GRAIN_UNIT_TON}>US Tons (t)</MenuItem>
</TextField>
</Grid2>
)}
</Grid2>
);
};
const content = () => {
return (
<DialogContent sx={{ margin: "auto", textAlign: "center" }}>
{ loadingComplete() ?
<CheckCircleOutline sx={{ fontSize: 64, color: green[500] }}/>
:
<CircularProgress size={64}/>
}
<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, such as adjusting unit preferences.
</Typography>
<br/>
<Typography variant="body2" >
If you are part of a team, you can select it here as well.
</Typography>
<br />
<Typography variant="body2" >
Set your unit preferences here now, they can be changed later from this menu:
</Typography>
{unitPreferences()}
</>
),
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"
})
steps.push({
title: "Tasks",
content: (
<>
<Typography variant="body2" >
You can view and create tasks here and assign a start and end time for them.
</Typography>
<br/>
<Typography>
If you are viewing as a team you will see the teams tasks, you can even assign tasks to team members.
</Typography>
</>
),
target: "#tour-tasks",
placement: "right"
})
//tasks step
if (getFeatures().visualFarm) {
steps.push({
title: "Visual Farm",
content: (
<>
<Typography variant="body2" >
You can view your Visual Farm here, it will allow tou to draw fields and plot bins an a map.
</Typography>
<br/>
<Typography>
If you are viewing as a team, your team's fields and bins will be displayed here instead.
</Typography>
</>
),
target: "#tour-visual-farm",
placement: "right",
disableBeacon: true,
})
steps.push({
title: "Fields",
content: (
<>
<Typography variant="body2" >
You can view your fields you have drawn on the map here.
</Typography>
<br/>
<Typography>
If you are viewing as a team, your team's fields will be displayed here instead.
</Typography>
</>
),
target: "#tour-field-list",
placement: "right",
disableBeacon: true,
})
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,
})
steps.push({
title: "Contracts",
content: (
<>
<Typography variant="body2" >
You can view and track any contracts you have created here.
</Typography>
<br/>
<Typography>
If you are viewing as a team, your team's contracts will be displayed here instead.
</Typography>
</>
),
target: "#tour-contracts",
placement: "right",
disableBeacon: true,
})
}
return steps;
};
const endTour = () => {
setIsTourRunning(false);
if (user) {
userAPI
.updateUser(user.id(), user.protobuf())
.then(() => dispatch({ key: "user", value: user }))
.catch((err: any) => console.error(err));
}
};
const closeDialog = () => {
setSignupDialogOpen(false)
setIsTourRunning(true)
}
useEffect(() => {
console.log(stepIndex)
}, [stepIndex])
return (
<PageContainer spacing={1}>
<Dialog open={signupDialogOpen} fullScreen={isMobile} >
<DialogTitle>
Finishing Signup
</DialogTitle>
<Divider sx={{ marginBottom: 2 }}/>
{ content() }
<DialogActions>
{loadingComplete() &&
<Button onClick={closeDialog}>
Done
</Button>
}
</DialogActions>
</Dialog>
<Tour
setStepIndex={setStepIndex}
spotlightStyle={{ marginTop: stepIndex!==1 ? 74 : 1 }}
run={isTourRunning} steps={getTourSteps()}
endTourCallback={endTour}
/>
</PageContainer>
)
}