711 lines
No EOL
23 KiB
TypeScript
711 lines
No EOL
23 KiB
TypeScript
import {
|
|
AccountBox,
|
|
Close,
|
|
ExpandLess,
|
|
ExpandMore,
|
|
Face,
|
|
Share as ShareIcon,
|
|
Notifications as NotificationsIcon,
|
|
Email as EmailIcon,
|
|
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, MenuItem, Radio, RadioGroup, Slider, TextField, Theme, ToggleButton, ToggleButtonGroup, Toolbar, Tooltip, Typography } from "@mui/material";
|
|
import ResponsiveDialog from "common/ResponsiveDialog";
|
|
import UserAvatar from "./UserAvatar";
|
|
import { useGlobalState, useSnackbar, useUserAPI } from "providers";
|
|
import { useEffect, useState } from "react";
|
|
import { makeStyles } from "@mui/styles";
|
|
import { blue } from "@mui/material/colors";
|
|
import ShareObject from "./ShareObject";
|
|
import { pond } from "protobuf-ts/pond";
|
|
import { User, userScope } from "models";
|
|
import SearchSelect from "common/SearchSelect";
|
|
import {Option as OptionType} from "common/SearchSelect";
|
|
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 UnitsIcon from "@mui/icons-material/Category";
|
|
import { setThemeType } from "theme";
|
|
import { getFeatures } from "services/whiteLabel";
|
|
import { setDistanceUnit, setGrainUnit, setPressureUnit, setTemperatureUnit } from "utils";
|
|
import FieldsIcon from "products/AgIcons/FieldsIcon";
|
|
|
|
const useStyles = makeStyles((theme: Theme) => {
|
|
return ({
|
|
title: {
|
|
marginLeft: theme.spacing(2),
|
|
marginRight: theme.spacing(2),
|
|
flex: 1
|
|
},
|
|
checkboxIcon: {
|
|
minWidth: theme.spacing(5),
|
|
[theme.breakpoints.up("sm")]: {
|
|
minWidth: theme.spacing(6)
|
|
},
|
|
[theme.breakpoints.up("md")]: {
|
|
minWidth: theme.spacing(7)
|
|
}
|
|
},
|
|
shareIcon: {
|
|
color: blue["600"],
|
|
"&:hover": {
|
|
color: blue["700"]
|
|
}
|
|
},
|
|
iconButton: {
|
|
margin: theme.spacing(1),
|
|
position: "absolute",
|
|
right: theme.spacing(6)
|
|
}
|
|
});
|
|
});
|
|
|
|
interface Props {
|
|
isOpen: boolean;
|
|
closeDialogCallback: () => void;
|
|
}
|
|
|
|
export default function UserSettings(props: Props) {
|
|
const themeMode = useThemeMode()
|
|
const { isOpen, closeDialogCallback } = props;
|
|
const [globalState, dispatch] = useGlobalState()
|
|
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 [mapsExpanded, setMapsExpanded] = useState<boolean>(false);
|
|
const [avatarUrl, setAvatarUrl] = useState<string>("");
|
|
const [sliderVal, setSliderVal] = useState(globalState.user.settings.mapZoom);
|
|
// const [themeValue, setThemeValue] = useState<"light" | "dark" | "system">(themeMode.mode)
|
|
|
|
useEffect(() => {
|
|
function changeIcon(icon: string | undefined) {
|
|
let updatedUser = User.clone(user);
|
|
updatedUser.settings.avatar = icon ? icon : user.settings.avatar;
|
|
setUser(updatedUser);
|
|
}
|
|
if (avatarUrl === "") {
|
|
changeIcon(undefined);
|
|
} else {
|
|
changeIcon(avatarUrl);
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [avatarUrl]);
|
|
|
|
const submit = () => {
|
|
userAPI
|
|
.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);
|
|
})
|
|
.catch((error: any) => {
|
|
error("Error occurred while updating your profile");
|
|
})
|
|
.finally(() => {
|
|
setIsLoading(false);
|
|
closeDialogCallback();
|
|
});
|
|
};
|
|
|
|
const setDefaultState = () => {
|
|
setIsLoading(false);
|
|
setUser(globalState.user);
|
|
};
|
|
|
|
const closeDialog = () => {
|
|
setDefaultState();
|
|
closeDialogCallback();
|
|
};
|
|
|
|
const loading = () => {
|
|
return <CircularProgress variant="indeterminate" color="primary" />;
|
|
};
|
|
|
|
const changeName = (name: string) => {
|
|
let updatedUser = User.clone(user);
|
|
updatedUser.settings.name = name;
|
|
setUser(updatedUser);
|
|
};
|
|
|
|
const changeTimezone = (tz: string) => {
|
|
let updatedUser = User.clone(user);
|
|
updatedUser.settings.timezone = tz;
|
|
setUser(updatedUser);
|
|
};
|
|
|
|
const changePhoneNumber = (newPhoneNumber: string) => {
|
|
let updatedUser = User.clone(user);
|
|
updatedUser.settings.phoneNumber = newPhoneNumber;
|
|
setUser(updatedUser);
|
|
};
|
|
|
|
const changeDefaultZoom = (value: number) => {
|
|
setSliderVal(value);
|
|
let updatedUser = User.clone(user);
|
|
updatedUser.settings.mapZoom = value;
|
|
setUser(updatedUser)
|
|
};
|
|
|
|
const mapSettings = () => {
|
|
return (
|
|
<Box display="flex">
|
|
<Typography>Map Zoom Level</Typography>
|
|
<Slider
|
|
valueLabelDisplay="auto"
|
|
value={sliderVal}
|
|
min={1}
|
|
max={16}
|
|
onChange={(_, val) => {
|
|
changeDefaultZoom(val as number);
|
|
}}
|
|
/>
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
const generalSettings = () => {
|
|
const { name, email, phoneNumber, timezone } = user.settings;
|
|
|
|
return (
|
|
<Grid2 container direction="column" spacing={4} justifyContent="flex-start" alignItems="flex-start">
|
|
<ShareObject
|
|
scope={userScope(user.id())}
|
|
label={""}
|
|
permissions={[
|
|
pond.Permission.PERMISSION_READ,
|
|
pond.Permission.PERMISSION_SHARE,
|
|
pond.Permission.PERMISSION_USERS,
|
|
pond.Permission.PERMISSION_WRITE
|
|
]}
|
|
isDialogOpen={sharing}
|
|
closeDialogCallback={() => setSharing(false)}
|
|
/>
|
|
<Tooltip title={"Share control of profile with another user"}>
|
|
<IconButton
|
|
aria-label="Share"
|
|
className={classes.iconButton}
|
|
onClick={() => setSharing(true)}>
|
|
<ShareIcon className={classes.shareIcon} />
|
|
</IconButton>
|
|
</Tooltip>
|
|
<Grid2>
|
|
<TextField
|
|
label="Preferred Name"
|
|
fullWidth
|
|
value={name}
|
|
helperText={"Email: " + email}
|
|
onChange={event => changeName(event.target.value.toString())}
|
|
/>
|
|
</Grid2>
|
|
<Grid2>
|
|
<FormControl component="fieldset">
|
|
<FormLabel component="legend" htmlFor="phone-number-input">
|
|
Phone Number
|
|
</FormLabel>
|
|
<MuiTelInput value={phoneNumber} onChange={changePhoneNumber} />
|
|
</FormControl>
|
|
</Grid2>
|
|
<Grid2 size={{ xs: 12 }}>
|
|
<Box>
|
|
<SearchSelect
|
|
label="Timezone"
|
|
selected={{ label: timezone, value: timezone } as OptionType}
|
|
options={moment.tz.names().map(tz => ({ label: tz, value: tz } as OptionType))}
|
|
changeSelection={(selected: OptionType | null) =>
|
|
changeTimezone(selected ? selected.value : "")
|
|
}
|
|
/>
|
|
</Box>
|
|
</Grid2>
|
|
</Grid2>
|
|
);
|
|
};
|
|
|
|
const handleChangeNotifyByDefault = (notifyByDefault: boolean) => {
|
|
let updatedUser = User.clone(user);
|
|
updatedUser.settings.notifyByDefault = notifyByDefault;
|
|
setUser(updatedUser);
|
|
};
|
|
|
|
const handleNotificationMethod = (
|
|
checked: boolean,
|
|
notificationMethod: pond.NotificationMethod
|
|
) => {
|
|
let updatedUser = User.clone(user);
|
|
let notificationMethods = updatedUser.settings.notificationMethods;
|
|
if (checked) {
|
|
if (!notificationMethods.includes(notificationMethod)) {
|
|
notificationMethods.push(notificationMethod);
|
|
}
|
|
} else {
|
|
notificationMethods = notificationMethods.filter(element => element !== notificationMethod);
|
|
}
|
|
updatedUser.settings.notificationMethods = notificationMethods;
|
|
setUser(updatedUser);
|
|
};
|
|
|
|
const notificationsSettings = () => {
|
|
const { notifyByDefault, notificationMethods } = user.settings;
|
|
|
|
return (
|
|
<Grid2 container direction="column" spacing={4} justifyContent="flex-start" alignItems="flex-start">
|
|
<Grid2>
|
|
<FormControl component="fieldset">
|
|
<FormLabel component="legend">Notification Preference</FormLabel>
|
|
<RadioGroup
|
|
aria-label="notification-default"
|
|
name="notificationDefault"
|
|
value={notifyByDefault}
|
|
onChange={event => handleChangeNotifyByDefault(event.target.value === "true")}>
|
|
<FormControlLabel value={true} control={<Radio />} label="Opt-in" />
|
|
<FormControlLabel value={false} control={<Radio />} label="Opt-out" />
|
|
</RadioGroup>
|
|
<FormHelperText>
|
|
{notifyByDefault
|
|
? "Notifications are enabled by default"
|
|
: "Notifications are disabled by default"}
|
|
</FormHelperText>
|
|
</FormControl>
|
|
</Grid2>
|
|
<Grid2>
|
|
<FormControl component="fieldset">
|
|
<FormLabel component="legend">Notification Methods</FormLabel>
|
|
<FormGroup>
|
|
<FormControlLabel
|
|
control={
|
|
<Checkbox
|
|
onChange={(_event, checked) =>
|
|
handleNotificationMethod(
|
|
checked,
|
|
pond.NotificationMethod.NOTIFICATION_METHOD_EMAIL
|
|
)
|
|
}
|
|
checked={
|
|
notificationMethods
|
|
? notificationMethods.includes(
|
|
pond.NotificationMethod.NOTIFICATION_METHOD_EMAIL
|
|
)
|
|
: false
|
|
}
|
|
/>
|
|
}
|
|
label={
|
|
<ListItem dense disableGutters>
|
|
<ListItemIcon className={classes.checkboxIcon}>
|
|
<EmailIcon />
|
|
</ListItemIcon>
|
|
<ListItemText primary="Email" />
|
|
</ListItem>
|
|
}
|
|
/>
|
|
|
|
<FormControlLabel
|
|
control={
|
|
<Checkbox
|
|
onChange={(_event, checked) =>
|
|
handleNotificationMethod(
|
|
checked,
|
|
pond.NotificationMethod.NOTIFICATION_METHOD_TEXT
|
|
)
|
|
}
|
|
checked={
|
|
notificationMethods
|
|
? notificationMethods.includes(
|
|
pond.NotificationMethod.NOTIFICATION_METHOD_TEXT
|
|
)
|
|
: false
|
|
}
|
|
/>
|
|
}
|
|
label={
|
|
<ListItem dense disableGutters>
|
|
<ListItemIcon className={classes.checkboxIcon}>
|
|
<TextIcon />
|
|
</ListItemIcon>
|
|
<ListItemText
|
|
primary="Text Message (SMS)"
|
|
secondary="Requires a valid phone number"
|
|
secondaryTypographyProps={{ variant: "caption" }}
|
|
/>
|
|
</ListItem>
|
|
}
|
|
/>
|
|
|
|
<FormControlLabel
|
|
control={
|
|
<Checkbox
|
|
onChange={(_event, checked) =>
|
|
handleNotificationMethod(
|
|
checked,
|
|
pond.NotificationMethod.NOTIFICATION_METHOD_APP
|
|
)
|
|
}
|
|
checked={
|
|
notificationMethods
|
|
? notificationMethods.includes(
|
|
pond.NotificationMethod.NOTIFICATION_METHOD_APP
|
|
)
|
|
: false
|
|
}
|
|
/>
|
|
}
|
|
label={
|
|
<ListItem dense disableGutters>
|
|
<ListItemIcon className={classes.checkboxIcon}>
|
|
<TextIcon />
|
|
</ListItemIcon>
|
|
<ListItemText
|
|
primary="In-App Notifications"
|
|
secondary="No email or phone number required"
|
|
secondaryTypographyProps={{ variant: "caption" }}
|
|
/>
|
|
</ListItem>
|
|
}
|
|
/>
|
|
</FormGroup>
|
|
</FormControl>
|
|
</Grid2>
|
|
</Grid2>
|
|
);
|
|
};
|
|
|
|
const avatarSettings = () => {
|
|
return <IconPicker url={user.settings.avatar} setUrl={setAvatarUrl} id={user.settings.id} />;
|
|
};
|
|
|
|
const handleChange = (event: React.MouseEvent<HTMLElement>, theme: "light" | "dark" | "system") => {
|
|
event.preventDefault()
|
|
themeMode.setMode(theme)
|
|
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 3:
|
|
grainUnit = pond.GrainUnit.GRAIN_UNIT_TONNE;
|
|
break;
|
|
case 4:
|
|
grainUnit = pond.GrainUnit.GRAIN_UNIT_TON;
|
|
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>
|
|
{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 themeToggle = () => {
|
|
return (
|
|
<ToggleButtonGroup
|
|
value={themeMode.mode}
|
|
exclusive
|
|
onChange={handleChange}
|
|
size="small"
|
|
sx={{margin:-1}}
|
|
>
|
|
<ToggleButton value="light">
|
|
light
|
|
</ToggleButton>
|
|
<ToggleButton value="system">
|
|
system
|
|
</ToggleButton>
|
|
<ToggleButton value="dark">
|
|
dark
|
|
</ToggleButton>
|
|
</ToggleButtonGroup>
|
|
)
|
|
}
|
|
|
|
const settingsList = () => {
|
|
return (
|
|
<List disablePadding>
|
|
<ListItem >
|
|
<ListItemIcon>
|
|
<Contrast />
|
|
</ListItemIcon>
|
|
<ListItemText primary={"Theme"} />
|
|
{themeToggle()}
|
|
</ListItem>
|
|
|
|
<Divider />
|
|
|
|
<ListItemButton onClick={() => setProfileExpanded(!profileExpanded)}>
|
|
<ListItemIcon>
|
|
<AccountBox />
|
|
</ListItemIcon>
|
|
<ListItemText primary={"Profile"} />
|
|
{profileExpanded ? <ExpandLess /> : <ExpandMore />}
|
|
</ListItemButton>
|
|
<Collapse in={profileExpanded} timeout="auto" unmountOnExit>
|
|
<List component="div">
|
|
<ListItem>
|
|
<ListItemText disableTypography inset>
|
|
{isLoading ? loading() : generalSettings()}
|
|
</ListItemText>
|
|
</ListItem>
|
|
</List>
|
|
</Collapse>
|
|
|
|
<Divider />
|
|
|
|
<ListItemButton onClick={() => setAvatarExpanded(!avatarExpanded)}>
|
|
<ListItemIcon>
|
|
<Face />
|
|
</ListItemIcon>
|
|
<ListItemText primary={"Display Picture"} />
|
|
{avatarExpanded ? <ExpandLess /> : <ExpandMore />}
|
|
</ListItemButton>
|
|
<Collapse in={avatarExpanded} timeout="auto" unmountOnExit>
|
|
<List
|
|
component="div"
|
|
sx={{
|
|
paddingLeft: 4,
|
|
paddingRight: 4
|
|
}}>
|
|
{isLoading ? loading() : avatarSettings()}
|
|
</List>
|
|
</Collapse>
|
|
|
|
<Divider />
|
|
|
|
<ListItemButton onClick={() => setNotificationsExpanded(!notificationsExpanded)}>
|
|
<ListItemIcon>
|
|
<NotificationsIcon />
|
|
</ListItemIcon>
|
|
<ListItemText primary="Notifications" />
|
|
{notificationsExpanded ? <ExpandLess /> : <ExpandMore />}
|
|
</ListItemButton>
|
|
<Collapse in={notificationsExpanded} timeout="auto" unmountOnExit>
|
|
<List component="div">
|
|
<ListItem>
|
|
<ListItemText disableTypography inset>
|
|
{isLoading ? loading() : notificationsSettings()}
|
|
</ListItemText>
|
|
</ListItem>
|
|
</List>
|
|
</Collapse>
|
|
|
|
<Divider />
|
|
|
|
<ListItemButton onClick={() => setUnitsExpanded(!unitsExpanded)}>
|
|
<ListItemIcon>
|
|
<UnitsIcon />
|
|
</ListItemIcon>
|
|
<ListItemText primary="Preferred Units" />
|
|
{unitsExpanded ? <ExpandLess /> : <ExpandMore />}
|
|
</ListItemButton>
|
|
<Collapse in={unitsExpanded} timeout="auto" unmountOnExit>
|
|
<List component="div">
|
|
<ListItem>
|
|
<ListItemText disableTypography inset>
|
|
{isLoading ? loading() : unitPreferences()}
|
|
</ListItemText>
|
|
</ListItem>
|
|
</List>
|
|
</Collapse>
|
|
|
|
<Divider />
|
|
|
|
<ListItemButton onClick={() => setMapsExpanded(!mapsExpanded)}>
|
|
<ListItemIcon>
|
|
<FieldsIcon />
|
|
</ListItemIcon>
|
|
<ListItemText primary={"Map Settings"} />
|
|
{mapsExpanded ? <ExpandLess /> : <ExpandMore />}
|
|
</ListItemButton>
|
|
<Collapse in={mapsExpanded} timeout="auto" unmountOnExit>
|
|
<List component="div">
|
|
<ListItem>
|
|
<ListItemText disableTypography inset>
|
|
{isLoading ? loading() : mapSettings()}
|
|
</ListItemText>
|
|
</ListItem>
|
|
</List>
|
|
</Collapse>
|
|
</List>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<ResponsiveDialog
|
|
maxWidth="sm"
|
|
fullWidth
|
|
open={isOpen}
|
|
onClose={closeDialog}
|
|
aria-labelledby="user-settings-dialog">
|
|
<AppBar position="relative">
|
|
<Toolbar>
|
|
<IconButton edge="start" color="inherit" onClick={closeDialog} aria-label="close">
|
|
<Close />
|
|
</IconButton>
|
|
<UserAvatar user={user} />
|
|
<Typography variant="h6" className={classes.title}>
|
|
User Settings
|
|
</Typography>
|
|
<Button color="inherit" onClick={submit}>
|
|
Save
|
|
</Button>
|
|
</Toolbar>
|
|
</AppBar>
|
|
{settingsList()}
|
|
</ResponsiveDialog>
|
|
);
|
|
} |