Merge branch 'master' of gitlab.com:brandx/bxt-app

This commit is contained in:
csawatzky 2025-06-26 13:28:13 -06:00
commit 28dbaa4be7
10 changed files with 351 additions and 63 deletions

View file

@ -53,7 +53,7 @@ export const appendToUrl = (appendage: number | string) => {
export default function Router() {
const { isAuthenticated, loginWithRedirect, isLoading } = useAuth0();
const { /*isAuthenticated, loginWithRedirect,*/ isLoading } = useAuth0();
const whiteLabel = getWhitelabel();
const [{ user }] = useGlobalState();
@ -283,12 +283,12 @@ export default function Router() {
};
if (isLoading) return null;
if (!isAuthenticated) {
loginWithRedirect()
return (
null
)
}
// if (!isAuthenticated) {
// loginWithRedirect()
// return (
// null
// )
// }
function ErrorFallback({ error }: { error: Error }) {
return <div>Something went wrong: {error.stack}</div>;
@ -302,6 +302,7 @@ export default function Router() {
{/* Redirects */}
<Route path="/callback" element={<Navigate to="/" />} />
{/* <Route path="/login" element={<Login />} /> */}
{/* <Route path="/team/:teamID" element={<Navigate to="/teams/:teamID" />} /> */}
{/* <Route path="/device/:deviceID" element={<Navigate to="/devices/:devicesID" />} /> */}
<Route path="" element={<Navigate to={whiteLabel.homePage ? whiteLabel.homePage : "/devices"} />} />

View file

@ -436,7 +436,7 @@ export default function SideNavigator(props: Props) {
</ListItemButton>
</Tooltip>
}
{isAg || isStreamline &&
{(isAg || isStreamline) &&
<Tooltip title="Marketplace" placement="right">
<ListItemButton
id="tour-marketplace"
@ -477,7 +477,7 @@ export default function SideNavigator(props: Props) {
{open && <ListItemText primary="Case New Holland" />}
</ListItemButton>
</Tooltip>
}
}
</List>
)
}

53
src/pages/Login.tsx Normal file
View file

@ -0,0 +1,53 @@
import { RedirectLoginOptions, useAuth0 } from "@auth0/auth0-react";
// import { useAuth } from "hooks";
import queryString from "query-string";
import { useEffect } from "react";
import { useLocation } from "react-router";
// import Loading from "./Loading";
import LoadingScreen from "app/LoadingScreen";
// interface Props {
// prevPath?: string;
// }
export default function Login() {
// const { prevPath } = props;
const location = useLocation();
const { loginWithRedirect } = useAuth0();
// const setRouteBeforeLogin = useCallback((): Promise<string> => {
// return new Promise(function(resolve) {
// localStorage.setItem("routeBeforeLogin", prevPath ? prevPath : "/");
// return resolve("success");
// });
// }, [prevPath]);
useEffect(() => {
const options: RedirectLoginOptions = {
authorizationParams: {} // Initialize here
};
if (location && location.search) {
let parsed = queryString.parse(location.search);
if (parsed.signup === "true") {
// if (options.authorizationParams) options.authorizationParams.screen_hint = "signup"; // 'signup' tells Auth0 to show the signup tab
options.authorizationParams!.screen_hint = "signup"; // 'signup' tells Auth0 to show the signup tab
options.authorizationParams!.prompt = "login";
}
if (parsed.email && parsed.email !== "") {
// if (options.authorizationParams) options.authorizationParams.login_hint = parsed.email.toString(); // prefill email
options.authorizationParams!.login_hint = parsed.email.toString(); // prefill email
}
}
// setRouteBeforeLogin().finally(() => {
console.log(options.authorizationParams)
loginWithRedirect(options);
// });
}, [location, loginWithRedirect]);
// return <LoadingScreen fullViewport={true} />;
return <LoadingScreen message="login redirect" />;
}

View file

@ -1,7 +1,7 @@
import { Container, SxProps, Theme } from "@mui/material";
import { makeStyles } from "@mui/styles";
import classNames from "classnames";
import { useMobile } from "hooks";
// import { useMobile } from "hooks";
import { PropsWithChildren } from "react";
const useStyles = makeStyles((theme: Theme) => ({
@ -44,11 +44,11 @@ interface Props extends PropsWithChildren{
export const PageContainer: React.FunctionComponent<Props> = (props: Props) => {
const classes = useStyles();
const isMobile = useMobile();
// const isMobile = useMobile();
const { children, fullViewport, isCenterCenter, sx } = props;
// let fullViewport = false
// let isCenterCenter = false
const spacing = props.spacing;
const spacing = props.spacing ? props.spacing : 2;
return (
<Container
className={classNames(

View file

@ -1,31 +1,55 @@
import { useAuth0 } from "@auth0/auth0-react";
import { RedirectLoginOptions, useAuth0 } from "@auth0/auth0-react";
import queryString from "query-string";
import { PropsWithChildren, useEffect } from "react";
interface Props extends PropsWithChildren<any>{
setToken: React.Dispatch<React.SetStateAction<string | undefined>>
prevPath?: string
}
export default function AuthWrapper(props: Props) {
const { children, setToken } = props;
const { isLoading, loginWithRedirect, isAuthenticated, getAccessTokenSilently } = useAuth0();
useEffect(() => {
if (!isAuthenticated && !isLoading) {
loginWithRedirect();
}
}, [isAuthenticated, loginWithRedirect, isLoading]);
const { children, setToken } = props;
const { isLoading, loginWithRedirect, isAuthenticated, getAccessTokenSilently } = useAuth0();
useEffect(() => {
if (isAuthenticated) getAccessTokenSilently().then(t => {
if (t.length < 1) {
loginWithRedirect()
}
setToken(t)
}).catch(() => {
// No token, go back to login
loginWithRedirect()
})
}, [setToken, isAuthenticated])
return children;
useEffect(() => {
const url = new URL(window.location.href);
const isManualLoginPage = url.pathname.includes("/login");
if (isManualLoginPage) {
const options: RedirectLoginOptions = {
authorizationParams: {} // Initialize here
};
let parsed = queryString.parse(url.search);
if (parsed.signup === "true") {
options.authorizationParams!.screen_hint = "signup";
options.authorizationParams!.prompt = "login";
}
if (parsed.email && parsed.email !== "") {
options.authorizationParams!.login_hint = parsed.email.toString(); // prefill email
}
loginWithRedirect(options)
} else if (!isAuthenticated && !isLoading) {
loginWithRedirect();
}
}, [isAuthenticated, loginWithRedirect, isLoading]);
useEffect(() => {
if (isAuthenticated) getAccessTokenSilently().then(t => {
if (t.length < 1) {
loginWithRedirect()
}
setToken(t)
}).catch(() => {
// No token, go back to login
loginWithRedirect()
})
}, [setToken, isAuthenticated])
return children;
}

View file

@ -146,6 +146,7 @@ export default function TeamActions(props: Props) {
permissions={permissions}
isDialogOpen={openState.share}
closeDialogCallback={() => setOpenState({ ...openState, share: false })}
useImitation={false}
/>
<ObjectUsers
scope={teamScope(key)}

View file

@ -1,11 +1,10 @@
import { Avatar, Box, Button, Checkbox, Divider, IconButton, ListItemIcon, ListItemText, Menu, MenuItem, PaletteColor, Theme, Tooltip, useTheme } from "@mui/material";
import { Avatar, Box, Button, Checkbox, Divider, ListItemIcon, ListItemText, Menu, MenuItem, PaletteColor, Theme, Tooltip, useTheme } from "@mui/material";
import { useGlobalState } from "../providers/StateContainer";
import { getSecondaryColour, getSignatureAccentColour } from "../services/whiteLabel";
import { makeStyles } from "@mui/styles";
import { LockOpen, Person, Lock, Settings, SupervisedUserCircle as TeamIcon, ExitToApp, PersonAdd } from "@mui/icons-material";
import { useState } from "react";
import UserTeamName from "./UserTeamName";
import ThemeIcon from "../common/ThemeIcon";
import { useAuth0 } from "@auth0/auth0-react";
import UserSettings from "./UserSettings";
import UserAvatar from "./UserAvatar";
@ -14,7 +13,7 @@ import { useNavigate } from "react-router-dom";
import TeamDialog from "teams/TeamDialog";
import AccessObject from "./AccessObject";
import { useSnackbar, useUserAPI } from "hooks";
import { useThemeMode } from "theme/AppThemeProvider";
// import { useThemeMode } from "theme/AppThemeProvider";
const useStyles = makeStyles((theme: Theme) => ({
accessIcon: {
@ -76,7 +75,7 @@ const useStyles = makeStyles((theme: Theme) => ({
export default function UserMenu() {
const { toggleMode } = useThemeMode()
// const { toggleMode } = useThemeMode()
const [{ user, team, as }, dispatch] = useGlobalState();
const { loginWithRedirect } = useAuth0();
const classes = useStyles();
@ -163,10 +162,6 @@ export default function UserMenu() {
<Lock className={classes.rightIcon} fontSize="small" />
)}
</Button>
<IconButton onClick={/*toggleMode*/ () => {}} className={classes.rightIcon} aria-label="Toggle Theme">
<ThemeIcon />
</IconButton>
</>
);
};

View file

@ -10,10 +10,10 @@ import {
Textsms as TextIcon,
Contrast,
} from "@mui/icons-material";
import { AppBar, Box, Button, Checkbox, CircularProgress, Collapse, Divider, FormControl, FormControlLabel, FormGroup, FormHelperText, FormLabel, Grid2, IconButton, List, ListItem, ListItemButton, ListItemIcon, ListItemText, Radio, RadioGroup, TextField, Theme, ToggleButton, ToggleButtonGroup, Toolbar, Tooltip, Typography } from "@mui/material";
import { AppBar, Box, Button, Checkbox, CircularProgress, Collapse, Divider, FormControl, FormControlLabel, FormGroup, FormHelperText, FormLabel, Grid2, IconButton, List, ListItem, ListItemButton, ListItemIcon, ListItemText, MenuItem, Radio, RadioGroup, TextField, Theme, ToggleButton, ToggleButtonGroup, Toolbar, Tooltip, Typography } from "@mui/material";
import ResponsiveDialog from "common/ResponsiveDialog";
import UserAvatar from "./UserAvatar";
import { useGlobalState, useUserAPI } from "providers";
import { useGlobalState, useSnackbar, useUserAPI } from "providers";
import { useEffect, useState } from "react";
import { makeStyles } from "@mui/styles";
import { blue } from "@mui/material/colors";
@ -26,8 +26,11 @@ import moment from "moment";
import { IconPicker } from "common/IconPicker";
import { MuiTelInput } from "mui-tel-input";
import { useThemeMode } from "theme/AppThemeProvider";
import ContractsIcon from "products/CommonIcons/contractIcon";
// import ContractsIcon from "products/CommonIcons/contractIcon";
import UnitsIcon from "@mui/icons-material/Category";
import { setThemeType } from "theme";
import { IsAdaptiveAgriculture } from "services/whiteLabel";
import { setDistanceUnit, setGrainUnit, setPressureUnit, setTemperatureUnit } from "utils";
const useStyles = makeStyles((theme: Theme) => {
return ({
@ -71,14 +74,16 @@ export default function UserSettings(props: Props) {
const [sharing, setSharing] = useState(false)
const userAPI = useUserAPI();
const classes = useStyles()
const { success } = useSnackbar();
const [isLoading, setIsLoading] = useState<boolean>(false);
const [user, setUser] = useState<User>(globalState.user);
const [profileExpanded, setProfileExpanded] = useState<boolean>(false);
const [avatarExpanded, setAvatarExpanded] = useState<boolean>(false);
const [notificationsExpanded, setNotificationsExpanded] = useState<boolean>(false);
const [unitsExpanded, setUnitsExpanded] = useState<boolean>(false);
const [avatarUrl, setAvatarUrl] = useState<string>("");
const [themeValue, setThemeValue] = useState<"light" | "dark" | "system">(themeMode.mode)
// const [themeValue, setThemeValue] = useState<"light" | "dark" | "system">(themeMode.mode)
useEffect(() => {
function changeIcon(icon: string | undefined) {
@ -99,11 +104,11 @@ export default function UserSettings(props: Props) {
.updateUser(user.id(), user.protobuf())
.then(() => {
dispatch({ key: "user", value: user });
// success("User settings successfully updated!");
// setTemperatureUnit(user.settings.temperatureUnit);
// setPressureUnit(user.settings.pressureUnit);
// setDistanceUnit(user.settings.distanceUnit);
// setGrainUnit(user.settings.grainUnit);
success("User settings successfully updated!");
setTemperatureUnit(user.settings.temperatureUnit);
setPressureUnit(user.settings.pressureUnit);
setDistanceUnit(user.settings.distanceUnit);
setGrainUnit(user.settings.grainUnit);
})
.catch((error: any) => {
error("Error occurred while updating your profile");
@ -362,6 +367,160 @@ export default function UserSettings(props: Props) {
setThemeType(theme)
};
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 changeDistanceUnit = (value: any) => {
let updatedUser = User.clone(user);
let distanceUnit: pond.DistanceUnit;
switch (value) {
case 1:
distanceUnit = pond.DistanceUnit.DISTANCE_UNIT_FEET;
break;
case 2:
distanceUnit = pond.DistanceUnit.DISTANCE_UNIT_METERS;
break;
default:
distanceUnit = pond.DistanceUnit.DISTANCE_UNIT_UNKNOWN;
break;
}
updatedUser.settings.distanceUnit = distanceUnit;
setUser(updatedUser);
};
const changeGrainUnit = (value: any) => {
let updatedUser = User.clone(user);
let grainUnit: pond.GrainUnit;
switch (value) {
case 1:
grainUnit = pond.GrainUnit.GRAIN_UNIT_BUSHELS;
break;
case 2:
grainUnit = pond.GrainUnit.GRAIN_UNIT_WEIGHT;
break;
default:
grainUnit = pond.GrainUnit.GRAIN_UNIT_UNKNOWN;
break;
}
updatedUser.settings.grainUnit = grainUnit;
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>
{IsAdaptiveAgriculture() && (
<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_WEIGHT}>Tonnes (mT)</MenuItem>
</TextField>
</Grid2>
)}
</Grid2>
);
};
const themeToggle = () => {
return (
<ToggleButtonGroup
@ -453,15 +612,15 @@ export default function UserSettings(props: Props) {
</List>
</Collapse>
{/* <Divider />
<Divider />
<ListItem button onClick={() => setUnitsExpanded(!unitsExpanded)}>
<ListItemButton onClick={() => setUnitsExpanded(!unitsExpanded)}>
<ListItemIcon>
<UnitsIcon />
</ListItemIcon>
<ListItemText primary="Preferred Units" />
{unitsExpanded ? <ExpandLess /> : <ExpandMore />}
</ListItem>
</ListItemButton>
<Collapse in={unitsExpanded} timeout="auto" unmountOnExit>
<List component="div">
<ListItem>
@ -472,7 +631,7 @@ export default function UserSettings(props: Props) {
</List>
</Collapse>
<Divider />
{/* <Divider />
<ListItem button onClick={() => setMapsExpanded(!mapsExpanded)}>
<ListItemIcon>