diff --git a/jams-react-client/src/api.tsx b/jams-react-client/src/api.tsx index 1dfb7b79b80bca9eb8756557ceb5dfdfa13d3571..8ee52e8b572f1a28369b3e4265dcc480596b8621 100644 --- a/jams-react-client/src/api.tsx +++ b/jams-react-client/src/api.tsx @@ -18,6 +18,7 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ +import { AxiosRequestConfig } from "axios"; import { url_path, url_port, @@ -31,18 +32,16 @@ import { api_path_blueprints, } from "./globalUrls"; -export default function configApiCall( - api_path, - request_type, - data, - credentials -) { +const configApiCall = ( + api_path: string, + request_type: string, + data: any, + credentials: null +): AxiosRequestConfig => { // build config call - const config = { + const config: AxiosRequestConfig = { url: url_path + ":" + url_port + api_path, method: request_type, - crossDomain: true, - dataType: "json", headers: {}, }; @@ -51,15 +50,15 @@ export default function configApiCall( const base64 = btoa( credentials["username"] + ":" + credentials["password"] ); - config["headers"]["Authorization"] = "Basic " + base64; + config.headers!["Authorization"] = "Basic " + base64; } const jwt = localStorage.getItem("access_token"); if (jwt) { - config["headers"]["Authorization"] = "Bearer " + jwt; + config.headers!["Authorization"] = "Bearer " + jwt; } - config["headers"]["Content-type"] = "application/json;charset=UTF-8"; + config.headers!["Content-type"] = "application/json;charset=UTF-8"; // pass data in the header if (data) { @@ -84,4 +83,6 @@ export default function configApiCall( } return config; -} +}; + +export default configApiCall; diff --git a/jams-react-client/src/components/CaSetup/CaSetup.tsx b/jams-react-client/src/components/CaSetup/CaSetup.tsx index e9d35137ecfdf7ec583fca389f0e7dfd412c8faf..d0d7d2f7269d4b793f3bc8b8edc4803574ceacce 100644 --- a/jams-react-client/src/components/CaSetup/CaSetup.tsx +++ b/jams-react-client/src/components/CaSetup/CaSetup.tsx @@ -246,7 +246,7 @@ export default function CaSetup(props) { } /> - <CountrySelect defaultValue={values.country} {...props} /> + <CountrySelect {...props} /> {touched.country && errors.country ? ( <span className="spanError">{errors.country}</span> ) : null} diff --git a/jams-react-client/src/components/CustomButtons/Button.tsx b/jams-react-client/src/components/CustomButtons/Button.tsx index 3b4aeaf555b291a1b633097928f930c7866b05b2..f695c959c83636ab0933bc50c65c2ec1782430d1 100644 --- a/jams-react-client/src/components/CustomButtons/Button.tsx +++ b/jams-react-client/src/components/CustomButtons/Button.tsx @@ -1,16 +1,26 @@ +import { FC, ReactNode } from "react"; import classNames from "classnames"; -// nodejs library to set properties for components -import PropTypes from "prop-types"; -// material-ui components +import Button, { ButtonProps } from "@mui/material/Button"; import { makeStyles } from "@mui/styles"; -import Button from "@mui/material/Button"; import styles from "assets/jss/material-dashboard-react/components/buttonStyle"; const useStyles = makeStyles(styles as any); -export default function RegularButton(props) { +interface RegularButtonProps extends ButtonProps { + size?: "small" | "large"; + simple?: boolean; + round?: boolean; + disabled?: boolean; + block?: boolean; + link?: boolean; + justIcon?: boolean; + className?: string; + children?: ReactNode; +} + +const RegularButton: FC<RegularButtonProps> = (props) => { const classes = useStyles(); const { color, @@ -23,7 +33,6 @@ export default function RegularButton(props) { link, justIcon, className, - muiClasses, ...rest } = props; const btnClasses = classNames({ @@ -39,32 +48,10 @@ export default function RegularButton(props) { [className]: className, }); return ( - <Button {...rest} classes={muiClasses} className={btnClasses}> + <Button {...rest} className={btnClasses}> {children} </Button> ); -} - -RegularButton.propTypes = { - color: PropTypes.oneOf([ - "primary", - "info", - "success", - "warning", - "danger", - "rose", - "white", - "transparent", - ]), - size: PropTypes.oneOf(["small", "large"]), - simple: PropTypes.bool, - round: PropTypes.bool, - disabled: PropTypes.bool, - block: PropTypes.bool, - link: PropTypes.bool, - justIcon: PropTypes.bool, - className: PropTypes.string, - // use this to pass the classes props from Material-UI - muiClasses: PropTypes.object, - children: PropTypes.node, }; + +export default RegularButton; diff --git a/jams-react-client/src/components/CustomUiPreview/CustomUiPreview.tsx b/jams-react-client/src/components/CustomUiPreview/CustomUiPreview.tsx index 65a0caccb9ad331e3b85a160c4f1b322f0144bf2..753c214a3f43a7edbc501179cd77aef8c2ca911c 100644 --- a/jams-react-client/src/components/CustomUiPreview/CustomUiPreview.tsx +++ b/jams-react-client/src/components/CustomUiPreview/CustomUiPreview.tsx @@ -103,7 +103,7 @@ const styles = { const useStyles = makeStyles(styles as any); interface CustomUiPreviewProps { - isOldPreview: boolean; + isOldPreview?: boolean; opacity: number; uiCustomization: UiCustomization; } diff --git a/jams-react-client/src/components/Devices/Devices.tsx b/jams-react-client/src/components/Devices/Devices.tsx index 06c36eaf79ef296f709f6e67c97eb377154ed8bd..7d9dd9c64e019700825771a64bb7beadd55c7127 100755 --- a/jams-react-client/src/components/Devices/Devices.tsx +++ b/jams-react-client/src/components/Devices/Devices.tsx @@ -1,4 +1,4 @@ -import { FC, useEffect, useState } from "react"; +import React, { FC, useEffect, useState } from "react"; import { useHistory } from "react-router-dom"; import classnames from "classnames"; @@ -49,7 +49,11 @@ const Devices: FC<DevicesProps> = ({ username }) => { const history = useHistory(); const [devices, setDevices] = useState<Device[]>([]); - const [selectedDevice, setSelectedDevice] = useState<Device>({}); + const [selectedDevice, setSelectedDevice] = useState<Device>({ + deviceId: "", + displayName: "", + revoked: false, + }); const [displayName, setDisplayName] = useState(""); const [openEdit, setOpenEdit] = useState(false); @@ -83,13 +87,19 @@ const Devices: FC<DevicesProps> = ({ username }) => { return device.revoked ? "Revoked" : "Active"; }; - const handleClickEdit = (e: MouseEvent, device: Device) => { + const handleClickEdit = ( + e: React.MouseEvent<HTMLButtonElement, MouseEvent>, + device: Device + ) => { e.preventDefault(); setOpenEdit(true); setSelectedDevice(device); }; - const handleClickRevoke = (e: MouseEvent, device: Device) => { + const handleClickRevoke = ( + e: React.MouseEvent<HTMLButtonElement, MouseEvent>, + device: Device + ) => { e.preventDefault(); setOpenRevoke(true); setSelectedDevice(device); @@ -98,7 +108,7 @@ const Devices: FC<DevicesProps> = ({ username }) => { const handleClose = () => { setOpenEdit(false); setOpenRevoke(false); - setSelectedDevice({}); + setSelectedDevice(undefined); }; const handleUpdate = () => { @@ -131,7 +141,7 @@ const Devices: FC<DevicesProps> = ({ username }) => { axios(requestConfig) .then(() => { - setSelectedDevice({}); + setSelectedDevice(undefined); setOpenEdit(false); }) .catch((error) => { @@ -160,7 +170,7 @@ const Devices: FC<DevicesProps> = ({ username }) => { axios(requestConfig) .then(() => { - setSelectedDevice({}); + setSelectedDevice(undefined); setOpenRevoke(false); }) .catch((error) => { diff --git a/jams-react-client/src/components/Devices/EditDeviceDialog.tsx b/jams-react-client/src/components/Devices/EditDeviceDialog.tsx index cc7fac87cc4ffb10daee07b49f3825d77999f9ce..b8d9c5a74b273a93678e8f9cf3a7eecbbd6cb6e6 100644 --- a/jams-react-client/src/components/Devices/EditDeviceDialog.tsx +++ b/jams-react-client/src/components/Devices/EditDeviceDialog.tsx @@ -9,6 +9,7 @@ import { import { Formik, Field } from "formik"; import i18next from "i18next"; import { Dispatch, FC, SetStateAction } from "react"; +import * as Yup from "yup"; import { Device } from "./Devices"; interface EditDeviceDialogProps { @@ -26,16 +27,9 @@ const EditDeviceDialog: FC<EditDeviceDialogProps> = ({ setDisplayName, handleUpdate, }) => { - /** - * Formik Validation - */ - const validateDisplayName = (displaynamevalue) => { - let error; - if (!displaynamevalue) { - error = "Required"; - } - return error; - }; + const validationSchema = Yup.object({ + displayName: Yup.string().required("Required"), + }); return ( <Dialog @@ -55,11 +49,13 @@ const EditDeviceDialog: FC<EditDeviceDialogProps> = ({ initialValues={{ displayName: selectedDevice.displayName, }} + onSubmit={handleUpdate} + validationSchema={validationSchema} > - {({ errors, touched, validateField }) => ( + {({ errors, touched }) => ( <form> <DialogContent> - <Field name="displayName" validate={validateDisplayName}> + <Field name="displayName"> {({ field }) => ( <div> <TextField @@ -87,12 +83,7 @@ const EditDeviceDialog: FC<EditDeviceDialogProps> = ({ <Button onClick={handleClose} color="primary"> {i18next.t("cancel", "Cancel") as string} </Button> - <Button - onClick={() => - validateField("displayName").then(() => handleUpdate()) - } - color="primary" - > + <Button type="submit" color="primary"> {i18next.t("update", "Update") as string} </Button> </DialogActions> diff --git a/jams-react-client/src/components/Drawer/Drawer.tsx b/jams-react-client/src/components/Drawer/Drawer.tsx index 4cfb0beaefffad4d231768b42685f34e9baa0778..e1db33d8a3299161a1437e86dd02b0aceb6146b7 100644 --- a/jams-react-client/src/components/Drawer/Drawer.tsx +++ b/jams-react-client/src/components/Drawer/Drawer.tsx @@ -144,7 +144,7 @@ const TemporaryDrawer: FC<TemporaryDrawerProps> = ({ })} role="presentation" > - <div className={classes.searchWrapper}> + <div> <CustomInput formControlProps={{ className: classes.margin + " " + classes.search, diff --git a/jams-react-client/src/components/Footer/Footer.tsx b/jams-react-client/src/components/Footer/Footer.tsx index a8bddce55a3cf60ec0979aaf1b959ea1810c52d2..32d790b0edc772c33b923e583c10b495c1a2d959 100755 --- a/jams-react-client/src/components/Footer/Footer.tsx +++ b/jams-react-client/src/components/Footer/Footer.tsx @@ -14,7 +14,7 @@ export default function Footer() { <p className={classes.right}> <span> JAMS Version {pjson.version.slice(0, 3)} - © - {1900 + new Date().getYear()}{" "} + {new Date().getFullYear()}{" "} <a href="https://savoirfairelinux.com" target="_blank" diff --git a/jams-react-client/src/components/FormikField/FormikField.tsx b/jams-react-client/src/components/FormikField/FormikField.tsx index 0f6df7aa04edaa314c3cd524cca18868210b7f62..3a4c2e034ba6b98d284876346e5614aa3532fbef 100644 --- a/jams-react-client/src/components/FormikField/FormikField.tsx +++ b/jams-react-client/src/components/FormikField/FormikField.tsx @@ -6,45 +6,34 @@ import FormControl from "@mui/material/FormControl"; import InputLabel from "@mui/material/InputLabel"; import Input from "@mui/material/Input"; -class FormikField extends Component { - render() { - return ( - <div className="FormikField"> - <FormControl - size="medium" - error={ - this.props.onKeyUpError || - <ErrorMessage name={this.props.name} /> === "" - } +const FormikField = (props) => { + return ( + <div className="FormikField"> + <FormControl size="medium" error={props.onKeyUpError} fullWidth> + <InputLabel htmlFor={props.name}> + {props.onKeyUpError ? ( + props.onKeyUpErrorMessage + ) : ( + <ErrorMessage name={props.name} /> + )} + </InputLabel> + <Field + placeholder={props.placeholder} + required={props.required} + name={props.name} + as={Input} + startAdornment={props.startAdornment} + endAdornment={props.endAdornment} + label={props.label} fullWidth - > - <InputLabel htmlFor={this.props.name}> - {this.props.onKeyUpError ? ( - this.props.onKeyUpErrorMessage - ) : "" || <ErrorMessage name={this.props.name} /> === "" ? ( - this.props.label - ) : ( - <ErrorMessage name={this.props.name} /> - )} - </InputLabel> - <Field - placeholder={this.props.placeholder} - required={this.props.required} - name={this.props.name} - as={Input} - startAdornment={this.props.startAdornment} - endAdornment={this.props.endAdornment} - label={this.props.label} - fullWidth - type={this.props.type} - autoComplete={this.props.autoComplete} - onKeyUp={(e) => this.props.handleChange(e.target.value)} - /> - </FormControl> - </div> - ); - } -} + type={props.type} + autoComplete={props.autoComplete} + onKeyUp={(e) => props.handleChange(e.target.value)} + /> + </FormControl> + </div> + ); +}; FormikField.propTypes = { startAdornment: PropTypes.element, diff --git a/jams-react-client/src/components/Grid/GridContainer.tsx b/jams-react-client/src/components/Grid/GridContainer.tsx index 8c3e97189b0663d04fd2d41684bc1e639a1c195c..012a39c675dbba41d505f4161d5a075c3f314509 100644 --- a/jams-react-client/src/components/Grid/GridContainer.tsx +++ b/jams-react-client/src/components/Grid/GridContainer.tsx @@ -1,7 +1,8 @@ import PropTypes from "prop-types"; // @mui/material components import { makeStyles } from "@mui/styles"; -import Grid from "@mui/material/Grid"; +import Grid, { GridProps } from "@mui/material/Grid"; +import { FC, ReactNode } from "react"; const styles = { grid: { @@ -12,7 +13,11 @@ const styles = { const useStyles = makeStyles(styles as any); -export default function GridContainer(props) { +interface GridContainerProps extends GridProps { + children: ReactNode; +} + +const GridContainer: FC<GridContainerProps> = (props) => { const classes = useStyles(); const { children, ...rest } = props; return ( @@ -20,8 +25,6 @@ export default function GridContainer(props) { {children} </Grid> ); -} - -GridContainer.propTypes = { - children: PropTypes.node, }; + +export default GridContainer; diff --git a/jams-react-client/src/components/Grid/GridItem.tsx b/jams-react-client/src/components/Grid/GridItem.tsx index 1dc6ffb40cf7c0649101c79e27093711714ee51b..eb69a955cedc6ba46658413b41d1d8cb6e8a5a5c 100644 --- a/jams-react-client/src/components/Grid/GridItem.tsx +++ b/jams-react-client/src/components/Grid/GridItem.tsx @@ -1,8 +1,7 @@ import { FC } from "react"; import * as React from "react"; import { makeStyles } from "@mui/styles"; -import { Grid, GridTypeMap } from "@mui/material"; -import { OverridableComponent } from "@mui/material/OverridableComponent"; +import { Grid, GridProps } from "@mui/material"; const styles = { grid: { @@ -12,7 +11,7 @@ const styles = { const useStyles = makeStyles(styles as any); -interface GridItemProps extends OverridableComponent<GridTypeMap> { +interface GridItemProps extends GridProps { children: React.ReactNode; } diff --git a/jams-react-client/src/components/LanguagePicker/LanguagePicker.tsx b/jams-react-client/src/components/LanguagePicker/LanguagePicker.tsx index 38a4619d1001a6680808929c0c65b55de1e93919..b485ce1e682b33cda5dc1ab035a0eacd224c7552 100644 --- a/jams-react-client/src/components/LanguagePicker/LanguagePicker.tsx +++ b/jams-react-client/src/components/LanguagePicker/LanguagePicker.tsx @@ -10,12 +10,17 @@ import i18next from "i18next"; import { useTranslation } from "react-i18next"; +interface Language { + code: string; + name: string; +} + export default function LanguagePicker(props) { const history = useHistory(); - const [language, setLanguage] = useState( + const [language, setLanguage] = useState<string>( i18next.language || window.localStorage.i18nextLng || "en" ); - const [languages, setLanguages] = useState([]); + const [languages, setLanguages] = useState<Language[]>([]); const { i18n } = useTranslation(); @@ -30,8 +35,10 @@ export default function LanguagePicker(props) { fetch("/available_languages.json") .then((res) => res.json()) .then((result) => { - const response = result.data.map(({ attributes }) => attributes); - let translates_languages = [ + const response: Language[] = result.data.map( + ({ attributes }: { attributes: Language }) => attributes + ); + const translates_languages: Language[] = [ { code: "en", name: "English" }, ...response, ]; diff --git a/jams-react-client/src/components/Navbars/Navbar.tsx b/jams-react-client/src/components/Navbars/Navbar.tsx index b41457fff7c8e20bdfdb010dbacf81144b6f2478..273389c71ffcee9bfac6d7f5214d9c101374aeff 100755 --- a/jams-react-client/src/components/Navbars/Navbar.tsx +++ b/jams-react-client/src/components/Navbars/Navbar.tsx @@ -25,11 +25,7 @@ export default function Header(props) { <AppBar className={classes.appBar + appBarClasses}> <Toolbar className={classes.container}> <div className={classes.flex}> - <Button - color="transparent" - href="#" - className={classes.title} - ></Button> + <Button color="info" className={classes.title}></Button> </div> <Hidden mdUp implementation="css"> <IconButton diff --git a/jams-react-client/src/components/PasswordDialog/PasswordDialog.tsx b/jams-react-client/src/components/PasswordDialog/PasswordDialog.tsx index 2b4e4fdaa576f93179bb351b3c10169e4f43e1b9..effc5b093ca67100a10b82853cfe033e73c7cbbf 100644 --- a/jams-react-client/src/components/PasswordDialog/PasswordDialog.tsx +++ b/jams-react-client/src/components/PasswordDialog/PasswordDialog.tsx @@ -271,7 +271,7 @@ const PasswordDialog: FC<PasswordDialogProps> = ({ </Button> <Button type="submit" - disable={!isValid && !dirty} + disabled={!isValid && !dirty} color="info" className={classes.whiteButtonText} autoFocus diff --git a/jams-react-client/src/components/Sidebar/Sidebar.tsx b/jams-react-client/src/components/Sidebar/Sidebar.tsx index 3a9fbc1d3cb68196e7f1b7c76ec0d43cfe2e8dd2..2acc3c872de6b95e75f346637951d664de8e207b 100755 --- a/jams-react-client/src/components/Sidebar/Sidebar.tsx +++ b/jams-react-client/src/components/Sidebar/Sidebar.tsx @@ -1,4 +1,4 @@ -import { createRef, useEffect, useState } from "react"; +import { FC, createRef, useEffect, useState } from "react"; import { Link, useHistory } from "react-router-dom"; import classNames from "classnames"; import PropTypes from "prop-types"; @@ -21,10 +21,25 @@ import styles from "assets/jss/material-dashboard-react/components/sidebarStyle" import auth from "auth"; import i18next from "i18next"; +import { Route } from "layouts/BaseLayout"; const useStyles = makeStyles(styles as any); -export default function Sidebar(props) { +interface SidebarProps { + updating: boolean; + snackbarMessage: string; + rtlActive: boolean; + handleQuery: () => void; + setOpenUpdate: (open: boolean) => void; + open: boolean; + handleDrawerToggle: () => void; + color: string; + logo: string; + image: string; + routes: Route[]; +} + +const Sidebar: FC<SidebarProps> = (props) => { const classes = useStyles(); const mainPanel = createRef(); const [open, setOpen] = useState(false); @@ -99,7 +114,7 @@ export default function Sidebar(props) { /> )} <ListItemText - primary={props.rtlActive ? prop.rtlName : prop.name} + primary={prop.name} className={classNames(classes.itemText, whiteFontClasses, { [classes.itemTextRTL]: props.rtlActive, })} @@ -216,15 +231,6 @@ export default function Sidebar(props) { </Hidden> </div> ); -} - -Sidebar.propTypes = { - rtlActive: PropTypes.bool, - handleDrawerToggle: PropTypes.func, - bgColor: PropTypes.oneOf(["purple", "blue", "green", "orange", "red"]), - logo: PropTypes.string, - image: PropTypes.string, - logoText: PropTypes.string, - routes: PropTypes.arrayOf(PropTypes.object), - open: PropTypes.bool, }; + +export default Sidebar; diff --git a/jams-react-client/src/components/Snackbar/BlueprintSnackbar.tsx b/jams-react-client/src/components/Snackbar/BlueprintSnackbar.tsx index 9c50167b1d334ccaf934e51f48eba4b5ef0f613f..9b1a0fe7e77f676972a5099a50d6e326a51722b0 100644 --- a/jams-react-client/src/components/Snackbar/BlueprintSnackbar.tsx +++ b/jams-react-client/src/components/Snackbar/BlueprintSnackbar.tsx @@ -1,16 +1,29 @@ -import { forwardRef, createRef, Dispatch, FC, SetStateAction } from "react"; +import { + forwardRef, + createRef, + Dispatch, + FC, + SetStateAction, + ForwardedRef, +} from "react"; import MuiAlert from "@mui/material/Alert"; import Snackbar from "@mui/material/Snackbar"; -import Slide from "@mui/material/Slide"; +import Slide, { SlideProps } from "@mui/material/Slide"; import { SnackbarProps } from "views/Blueprint/PolicyDataContext"; // https://stackoverflow.com/a/67961603 -const Alert = forwardRef(function Alert(props, ref) { +const Alert = forwardRef(function Alert( + props: any, + ref: ForwardedRef<HTMLDivElement> +) { return <MuiAlert elevation={6} ref={ref} variant="filled" {...props} />; }); -const SlideTransition = forwardRef(function SlideTransition(props, ref) { +const SlideTransition = forwardRef(function SlideTransition( + props: SlideProps, + ref +) { return <Slide ref={ref} {...props} direction="left" />; }); @@ -23,7 +36,7 @@ export const BlueprintSnackbar: FC<BlueprintSnackbarProps> = ({ snackbar, setSnackbar, }) => { - const snackbarRef = createRef(null); + const snackbarRef = createRef(); const handleClose = () => { setSnackbar((state) => ({ ...state, open: false })); diff --git a/jams-react-client/src/i18n.tsx b/jams-react-client/src/i18n.tsx index d251bfcb587b4afbf8a0afed05a040f29f2d65dc..72f43d2f8385eb4af53fea77dd4892a0b2a50232 100644 --- a/jams-react-client/src/i18n.tsx +++ b/jams-react-client/src/i18n.tsx @@ -9,9 +9,6 @@ i18next .use(Backend) .init({ fallbackLng: "en", - lookupCookie: "i18next", - lookupLocalStorage: "i18nextLng", - lookupSessionStorage: "i18nextLng", debug: false, detection: { order: ["cookie", "localStorage", "sessionStorage", "navigator"], diff --git a/jams-react-client/src/layouts/BaseLayout.tsx b/jams-react-client/src/layouts/BaseLayout.tsx index 816200152a2a797140794f77ae7e5e0d48e74edd..86483d293d4b6b29ebbd2a9a534bc7008b3518ec 100644 --- a/jams-react-client/src/layouts/BaseLayout.tsx +++ b/jams-react-client/src/layouts/BaseLayout.tsx @@ -1,4 +1,4 @@ -import { createRef, useEffect, useState } from "react"; +import { FC, ReactNode, createRef, useEffect, useState } from "react"; // creates a beautiful scrollbar import PerfectScrollbar from "perfect-scrollbar"; import "perfect-scrollbar/css/perfect-scrollbar.css"; @@ -46,15 +46,24 @@ let ps; const useStyles = makeStyles(styles as any); +export interface Route { + path: string; + name: string; + icon: FC<any>; + component: FC<any>; + layout: string; + admin?: boolean; +} + export default function Admin(props) { // styles const classes = useStyles(); // ref to help us initialize PerfectScrollbar on windows devices - const mainPanel = createRef(); + const mainPanel = createRef<HTMLDivElement>(); // states and functions const [mobileOpen, setMobileOpen] = useState(false); const [open, setOpen] = useState(false); - const [message, setMessage] = useState(false); + const [message, setMessage] = useState(""); const [openUpdate, setOpenUpdate] = useState(false); const [dialogMessage, setDialogMessage] = useState(""); const [messageYes, setMessageYes] = useState(""); @@ -63,7 +72,7 @@ export default function Admin(props) { const [query, setQuery] = useState(false); const [snackbarMessage, setSnackbarMessage] = useState(""); - const Routes = [ + const Routes: Route[] = [ { path: `/user/${auth.getUsername()}`, name: i18next.t("myprofile", "My profile") as string, @@ -237,7 +246,6 @@ export default function Admin(props) { </Dialog> <Sidebar routes={Routes} - logoText={"Jams"} logo={logo} image={bgImage} handleDrawerToggle={handleDrawerToggle} @@ -247,6 +255,7 @@ export default function Admin(props) { updating={updating} snackbarMessage={snackbarMessage} setOpenUpdate={setOpenUpdate} + rtlActive={false} /> <div className={classes.mainPanel} ref={mainPanel}> diff --git a/jams-react-client/src/layouts/ListLayout.tsx b/jams-react-client/src/layouts/ListLayout.tsx index 3ecd4c97cf9e298f6bccb08da286c7031fcc6ca0..89552f2c65ca147513d6e5d8d9fa9272b129f1cd 100644 --- a/jams-react-client/src/layouts/ListLayout.tsx +++ b/jams-react-client/src/layouts/ListLayout.tsx @@ -41,6 +41,7 @@ import DialogContentText from "@mui/material/DialogContentText/DialogContentText import Button from "@mui/material/Button"; import i18next from "i18next"; +import { Route } from "./BaseLayout"; let ps; @@ -50,11 +51,11 @@ export default function Admin(props) { // styles const classes = useStyles(); // ref to help us initialize PerfectScrollbar on windows devices - const mainPanel = createRef(); + const mainPanel = createRef<HTMLDivElement>(); // states and functions const [mobileOpen, setMobileOpen] = useState(false); const [open, setOpen] = useState(false); - const [message, setMessage] = useState(false); + const [message, setMessage] = useState(""); const [openUpdate, setOpenUpdate] = useState(false); const [dialogMessage, setDialogMessage] = useState(""); const [messageYes, setMessageYes] = useState(""); @@ -63,7 +64,7 @@ export default function Admin(props) { const [query, setQuery] = useState(false); const [snackbarMessage, setSnackbarMessage] = useState(""); - const Routes = [ + const Routes: Route[] = [ { path: `/user/${auth.getUsername()}`, name: i18next.t("myprofile", "My profile") as string, @@ -239,7 +240,6 @@ export default function Admin(props) { </Dialog> <Sidebar routes={Routes} - logoText={"Jams"} logo={logo} image={bgImage} handleDrawerToggle={handleDrawerToggle} @@ -249,6 +249,7 @@ export default function Admin(props) { updating={updating} snackbarMessage={snackbarMessage} setOpenUpdate={setOpenUpdate} + rtlActive={false} /> <div className={classes.mainPanel} ref={mainPanel}> diff --git a/jams-react-client/src/layouts/SignIn.tsx b/jams-react-client/src/layouts/SignIn.tsx index c08118b3fc1ea82d62c4e7a24a9bef03b555af99..1a5f8b8c72d3fa5a8f6c054047acffc528ad0356 100644 --- a/jams-react-client/src/layouts/SignIn.tsx +++ b/jams-react-client/src/layouts/SignIn.tsx @@ -90,7 +90,7 @@ export default function SignIn() { password: values.password, }; auth.login(jsonData, () => { - if (auth.authenticated && auth.access_token !== "") { + if (auth.authenticated) { auth.checkLastKnownStep(() => { auth.checkDirectoryType(() => { if (auth.isServerInstalled()) { diff --git a/jams-react-client/src/layouts/SignUp.tsx b/jams-react-client/src/layouts/SignUp.tsx index fdbd27e4db48305bd1cfc2a7e21f99ef0a3c203b..182ee85d12ab8f51b2eff5c00393cdbe6f2f5d00 100644 --- a/jams-react-client/src/layouts/SignUp.tsx +++ b/jams-react-client/src/layouts/SignUp.tsx @@ -51,7 +51,7 @@ const useStyles = makeStyles((theme: Theme) => ({ marginTop: theme.spacing(3), marginBottom: theme.spacing(3), padding: theme.spacing(2), - [theme.breakpoints.up(600 + theme.spacing(3) * 2)]: { + [theme.breakpoints.up(700)]: { marginTop: theme.spacing(6), marginBottom: theme.spacing(6), padding: theme.spacing(3), diff --git a/jams-react-client/src/views/Blueprint/ColorPickerPopup.tsx b/jams-react-client/src/views/Blueprint/ColorPickerPopup.tsx index c37b7063afa7dc2ed2b7870b0de8e9b11a681a2e..3f316be583f02872946daaa4de11417cc1025314 100644 --- a/jams-react-client/src/views/Blueprint/ColorPickerPopup.tsx +++ b/jams-react-client/src/views/Blueprint/ColorPickerPopup.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { FC, useState } from "react"; import { makeStyles } from "@mui/styles"; import { HexAlphaColorPicker, HexColorPicker } from "react-colorful"; @@ -33,7 +33,17 @@ const styles = { const useStyles = makeStyles(styles as any); -export default function ColorPickerPopup({ hasAlphaChannel, color, onChange }) { +interface ColorPickerPopupProps { + hasAlphaChannel?: boolean; + color: string; + onChange: (color: string) => void; +} + +const ColorPickerPopup: FC<ColorPickerPopupProps> = ({ + hasAlphaChannel, + color, + onChange, +}) => { const classes = useStyles(); const [displayColorPicker, setDisplayColorPicker] = useState(false); @@ -69,4 +79,6 @@ export default function ColorPickerPopup({ hasAlphaChannel, color, onChange }) { )} </div> ); -} +}; + +export default ColorPickerPopup; diff --git a/jams-react-client/src/views/Blueprint/EditBlueprintPermissions.tsx b/jams-react-client/src/views/Blueprint/EditBlueprintPermissions.tsx index d1511c11084857f3910c4f776fe9105f5286936e..819e11c0e527113fb88b0da9088577159026ea42 100644 --- a/jams-react-client/src/views/Blueprint/EditBlueprintPermissions.tsx +++ b/jams-react-client/src/views/Blueprint/EditBlueprintPermissions.tsx @@ -120,7 +120,7 @@ export default function EditBlueprintPermissions(props) { allowLookup, } = policyData; - const searchUsers = (value) => { + const searchUsers = (value?: string) => { axios( configApiCall( api_path_get_user_directory_search, diff --git a/jams-react-client/src/views/Blueprint/EditBlueprintUi.tsx b/jams-react-client/src/views/Blueprint/EditBlueprintUi.tsx index c8368b229660f92da0ba30b6983e944310a9ac08..f009d50ca4c03f0442659c079f488624374aa91f 100644 --- a/jams-react-client/src/views/Blueprint/EditBlueprintUi.tsx +++ b/jams-react-client/src/views/Blueprint/EditBlueprintUi.tsx @@ -22,7 +22,10 @@ import i18next from "i18next"; import { CustomUiPreview } from "components/CustomUiPreview/CustomUiPreview"; import EditBlueprintUiForm from "./EditBlueprintUiForm"; -import { DEFAULT_UI_CUSTOMIZATION } from "./policyData.constants"; +import { + DEFAULT_UI_CUSTOMIZATION, + UiCustomization, +} from "./policyData.constants"; import { PolicyDataContext } from "./PolicyDataContext"; const styles = { @@ -104,7 +107,7 @@ export default function EditBlueprintUi({ blueprintName }) { const [oldUiCustomization, setOldUiCustomization] = useState(uiCustomization); const [opacity, setOpacity] = useState(0); - const handleUpdateUi = (field: string, value: any) => { + const handleUpdateUi = (field: string | UiCustomization, value?: any) => { let newUiCustomization; if (typeof field === "object") { @@ -206,7 +209,7 @@ export default function EditBlueprintUi({ blueprintName }) { /> <CustomUiPreview isOldPreview - opacity="1" + opacity={1} uiCustomization={oldUiCustomization} /> </Grid> diff --git a/jams-react-client/src/views/Blueprint/PolicyDataContext.tsx b/jams-react-client/src/views/Blueprint/PolicyDataContext.tsx index 9fa77385823f5130b9a5ed6c2413d7d36f626f45..894bcccaa1f11f6ae54c61491ea34f377da98ca9 100644 --- a/jams-react-client/src/views/Blueprint/PolicyDataContext.tsx +++ b/jams-react-client/src/views/Blueprint/PolicyDataContext.tsx @@ -16,7 +16,7 @@ export interface SnackbarProps { interface PolicyDataProp { policyData: PolicyData; - updatePolicyData: (field: string, value: string) => void; + updatePolicyData: (field: string, value: any) => void; snackbar: SnackbarProps; setSnackbar: (snackbar: SnackbarProps) => void; } @@ -61,7 +61,7 @@ export const PolicyDataContextProvider: FC<Props> = ({ }); }, [blueprintName]); - const updatePolicyData = (field: string, value: string) => { + const updatePolicyData = (field: string, value: any) => { _updatePolicyData( blueprintName, policyData, diff --git a/jams-react-client/src/views/Blueprint/parsePolicyData.tsx b/jams-react-client/src/views/Blueprint/parsePolicyData.tsx index dde06e90354f3518561df3eea2181292270c5b6a..8087581ae20ff9acfd81261207c36c444b058057 100644 --- a/jams-react-client/src/views/Blueprint/parsePolicyData.tsx +++ b/jams-react-client/src/views/Blueprint/parsePolicyData.tsx @@ -5,7 +5,11 @@ import { api_path_get_ns_name_from_addr, api_path_get_user_profile, } from "../../globalUrls"; -import { DEFAULT_UI_CUSTOMIZATION, PolicyData } from "./policyData.constants"; +import { + DEFAULT_UI_CUSTOMIZATION, + PolicyData, + UiCustomization, +} from "./policyData.constants"; const getModerators = async (defaultModerators: string) => { const moderators = []; @@ -78,7 +82,7 @@ const setCustomizationSettings = (policyData) => { } const result = JSON.parse(policyData.uiCustomization); - const ui = { isCustomizationEnabled: true }; + const ui: Partial<UiCustomization> = { isCustomizationEnabled: true }; ui.hasTitle = result.title !== ""; ui.title = result.title; diff --git a/jams-react-client/src/views/Blueprint/updatePolicyData.tsx b/jams-react-client/src/views/Blueprint/updatePolicyData.tsx index 8066a59d7c7a576dd9e95efc877cdbc3f463b209..c1ecc173e19c0a4011bc393af38d8098b44ef0ff 100644 --- a/jams-react-client/src/views/Blueprint/updatePolicyData.tsx +++ b/jams-react-client/src/views/Blueprint/updatePolicyData.tsx @@ -89,7 +89,7 @@ const updateUiCustomization = (data) => { logoUrl, logoSize, } = data.uiCustomization; - const ui = {}; + const ui: Partial<ServerUiCustomization> = {}; if (hasTitle === false) { ui.title = ""; diff --git a/jams-react-client/src/views/Contacts/Contacts.tsx b/jams-react-client/src/views/Contacts/Contacts.tsx index cfcaed830637feb4886f96f2d94371658374f603..af726a06f6e210ed7b01d11ba60917a489a1b6e3 100644 --- a/jams-react-client/src/views/Contacts/Contacts.tsx +++ b/jams-react-client/src/views/Contacts/Contacts.tsx @@ -92,12 +92,12 @@ export default function Users(props) { const [loading, setLoading] = useState(false); const [progress, setProgress] = useState(0); const [openDrawer, setOpenDrawer] = useState(false); - const [removedContact, setRemovedContact] = useState(); + const [removedContact, setRemovedContact] = useState(""); const [removedContactName, setRemovedContactName] = useState(); const [open, setOpen] = useState(false); const [allowedToAdd, setAllowedToAdd] = useState(true); - const searchContacts = (value) => { + const searchContacts = (value?: string) => { axios( configApiCall( api_path_get_user_directory_search, @@ -161,6 +161,7 @@ export default function Users(props) { axios( configApiCall( api_path_get_ns_name_from_addr + contact.uri, + "GET", null, null ) @@ -503,7 +504,7 @@ export default function Users(props) { )} </GridItem> ))} - {contacts === [] && + {contacts.length === 0 && ((props.username + i18next.t("has_no_contacts", " has no contacts")) as string)} </GridContainer> diff --git a/jams-react-client/src/views/Groups/EditGroup.tsx b/jams-react-client/src/views/Groups/EditGroup.tsx index ef28b95171cd267781cba352cdf625328afaeb03..9b0503a4edd143bf132c610e31760296287ea0fd 100644 --- a/jams-react-client/src/views/Groups/EditGroup.tsx +++ b/jams-react-client/src/views/Groups/EditGroup.tsx @@ -64,9 +64,10 @@ import auth from "auth"; import { debounce } from "lodash"; import { getBlueprintsOptions } from "./getBlueprintsOptions"; +import { ClassNameMap } from "@mui/material"; const useStyles = makeStyles(() => ({ - ...devicesStyle, + ...(devicesStyle as any), ...dashboardStyle, root: { flexGrow: 1, @@ -112,7 +113,7 @@ const useStyles = makeStyles(() => ({ })); export default function EditGroup(props) { - const classes = useStyles(); + const classes: ClassNameMap<any> = useStyles(); const history = useHistory(); const [name, setName] = useState(""); @@ -209,7 +210,7 @@ export default function EditGroup(props) { getBlueprintsOptions(blueprints) ); - const updateGroup = (blueprintValue) => { + const updateGroup = (blueprintValue?: string) => { const data = { name: newName, blueprint: blueprintValue ? blueprintValue : selectedBlueprint.label, @@ -231,7 +232,7 @@ export default function EditGroup(props) { }); }; - const searchUsers = (value) => { + const searchUsers = (value?: string) => { axios( configApiCall( api_path_get_user_directory_search, diff --git a/jams-react-client/src/views/Settings/General.tsx b/jams-react-client/src/views/Settings/General.tsx index 095619942906aa5d080c06830a67e4076ab768d2..fba0f28aa5d42655c47d31ce4824a44ef1cab7ff 100644 --- a/jams-react-client/src/views/Settings/General.tsx +++ b/jams-react-client/src/views/Settings/General.tsx @@ -207,7 +207,7 @@ export default function General(props) { onKeyUpErrorMessage="" /> {touched.password && errors.password ? ( - <span>{errors.password}</span> + <span>{errors.password.toString()}</span> ) : null} <FormikField @@ -245,7 +245,7 @@ export default function General(props) { onKeyUpErrorMessage="" /> {touched.confirmPassword && errors.confirmPassword ? ( - <span>{errors.confirmPassword}</span> + <span>{errors.confirmPassword.toString()}</span> ) : null} <Button @@ -311,7 +311,6 @@ export default function General(props) { color="primary" fullWidth disabled={!isValid || !dirty} - className={classes.submit} > { i18next.t( diff --git a/jams-react-client/src/views/UserProfile/DisplayUserProfile.tsx b/jams-react-client/src/views/UserProfile/DisplayUserProfile.tsx index 972d59e5ac3fdef99cd0f16d3a9a05703e66d9dc..e528350b227839b2e5fba4aea166c1dd6649111d 100644 --- a/jams-react-client/src/views/UserProfile/DisplayUserProfile.tsx +++ b/jams-react-client/src/views/UserProfile/DisplayUserProfile.tsx @@ -184,6 +184,7 @@ export interface UserProfile { phoneNumberExtension: string; faxNumber: string; mobileNumber: string; + jamiId?: string; } export interface GroupMembership { @@ -197,7 +198,7 @@ export interface Group { blueprint: string; } -interface UserGroup { +interface UserGroupMapping { groupId: string; username: string; } @@ -208,7 +209,18 @@ const DisplayUserProfile: FC<DisplayUserProfileProps> = ({ }) => { const classes = useStyles(); const history = useHistory(); - const [user, setUser] = useState<UserProfile>({}); + const [user, setUser] = useState<UserProfile>({ + username: "", + firstName: "", + lastName: "", + email: "", + profilePicture: "", + organization: "", + phoneNumber: "", + phoneNumberExtension: "", + faxNumber: "", + mobileNumber: "", + }); const [groupMemberships, setGroupMemberships] = useState<GroupMembership[]>( [] ); @@ -257,6 +269,7 @@ const DisplayUserProfile: FC<DisplayUserProfileProps> = ({ }, [history, username]); const getAdminUserGroups = () => { + // TODO do this in a single sql query on the server, with a JOIN axios( configApiCall( api_path_get_admin_user_groups + username, @@ -265,15 +278,22 @@ const DisplayUserProfile: FC<DisplayUserProfileProps> = ({ null ) ).then((userGroups) => { - const userGroupsData: UserGroup[] = userGroups.data; - userGroupsData.forEach((group) => { + const userGroupsData: UserGroupMapping[] = userGroups.data; + const promises = userGroupsData.map((group) => axios( configApiCall(api_path_get_group + group.groupId, "GET", null, null) ).then((groupInfo) => { - group["name"] = groupInfo.data.name; - }); + const g: GroupMembership = { + groupId: group.groupId, + name: groupInfo.data.name, + }; + return g; + }) + ); + + Promise.all(promises).then((groupMemberships) => { + setGroupMemberships(groupMemberships); }); - setGroupMemberships(userGroupsData); }); }; diff --git a/jams-react-client/src/views/UserProfile/EditCreateUserProfile.tsx b/jams-react-client/src/views/UserProfile/EditCreateUserProfile.tsx index 6de3cb0f8e6d652a7535ff9a17dfa1a8f3f59c92..b037e73a8e533c2045e981f54ad5ce596ad39537 100644 --- a/jams-react-client/src/views/UserProfile/EditCreateUserProfile.tsx +++ b/jams-react-client/src/views/UserProfile/EditCreateUserProfile.tsx @@ -74,6 +74,7 @@ import LinearProgress from "@mui/material/LinearProgress"; import i18next from "i18next"; import generator from "generate-password-browser"; +import { UserProfile } from "./DisplayUserProfile"; const styles = (theme) => ({ ...dashboardStyle, @@ -218,6 +219,11 @@ const styles = (theme) => ({ const useStyles = makeStyles(styles as any); +interface UserProfileForm extends UserProfile { + password?: string; + confirmPassword?: string; +} + const toBase64 = (file: File): Promise<string | undefined> => new Promise((resolve, reject) => { const reader = new FileReader(); @@ -258,7 +264,7 @@ export default function EditCreateUserProfile(props) { const intialyGeneratedPassword = passwordGenerator(); - const [initialValues, setInitialValues] = useState({ + const [initialValues, setInitialValues] = useState<UserProfileForm>({ username: "", password: intialyGeneratedPassword, confirmPassword: intialyGeneratedPassword, @@ -520,7 +526,9 @@ export default function EditCreateUserProfile(props) { 2, i18next.t("last_name_is_too_short", "Last Name is too short!") as string ), - email: Yup.string().email(i18next.t("invalid_email", "Invalid email!")), + email: Yup.string().email( + i18next.t("invalid_email", "Invalid email!") as string + ), profilePicture: Yup.string(), organization: Yup.string().min( 2, @@ -586,7 +594,7 @@ export default function EditCreateUserProfile(props) { step={0.1} aria-labelledby="Zoom" className={classes.slider} - onChange={(e, zoom) => setZoom(zoom)} + onChange={(e, zoom: any) => setZoom(zoom)} /> </div> <div className={classes.sliderContainer}> @@ -603,7 +611,7 @@ export default function EditCreateUserProfile(props) { step={1} aria-labelledby="Rotation" className={classes.slider} - onChange={(e, rotation) => setRotation(rotation)} + onChange={(e, rotation: any) => setRotation(rotation)} /> </div> </div> @@ -823,16 +831,10 @@ export default function EditCreateUserProfile(props) { </Grid> )} {props.createUser && ( - <Grid - item - align="left" - xs={12} - sm={12} - md={6} - ></Grid> + <Grid item xs={12} sm={12} md={6}></Grid> )} {props.createUser && ( - <Grid item align="left" xs={12} sm={12} md={6}> + <Grid item xs={12} sm={12} md={6}> <Button variant="contained" color="primary" @@ -897,7 +899,13 @@ export default function EditCreateUserProfile(props) { ) : null} </Grid> )} - <Grid item align="center" xs={12} sm={12} md={6}> + <Grid + item + alignContent="center" + xs={12} + sm={12} + md={6} + > <FormikField name="firstName" label={ @@ -917,7 +925,13 @@ export default function EditCreateUserProfile(props) { onKeyUpErrorMessage="" /> </Grid> - <Grid item align="center" xs={12} sm={12} md={6}> + <Grid + item + alignContent="center" + xs={12} + sm={12} + md={6} + > <FormikField name="lastName" label={ @@ -936,7 +950,7 @@ export default function EditCreateUserProfile(props) { onKeyUpErrorMessage="" /> </Grid> - <Grid item align="center" xs={12} sm={12} md={6}> + <Grid item alignSelf="center" xs={12} sm={12} md={6}> <FormikField name="email" label={i18next.t("email", "Email") as string} @@ -953,7 +967,7 @@ export default function EditCreateUserProfile(props) { onKeyUpErrorMessage="" /> </Grid> - <Grid item align="center" xs={12} sm={12} md={6}> + <Grid item alignSelf="center" xs={12} sm={12} md={6}> <FormikField name="organization" label={ @@ -976,7 +990,7 @@ export default function EditCreateUserProfile(props) { onKeyUpErrorMessage="" /> </Grid> - <Grid item align="center" xs={12} sm={12} md={6}> + <Grid item alignSelf="center" xs={12} sm={12} md={6}> <FormikField name="faxNumber" label={ @@ -996,7 +1010,7 @@ export default function EditCreateUserProfile(props) { onKeyUpErrorMessage="" /> </Grid> - <Grid item align="center" xs={12} sm={12} md={6}> + <Grid item alignSelf="center" xs={12} sm={12} md={6}> <FormikField name="phoneNumber" label={ @@ -1019,7 +1033,7 @@ export default function EditCreateUserProfile(props) { onKeyUpErrorMessage="" /> </Grid> - <Grid item align="center" xs={12} sm={12} md={6}> + <Grid item alignSelf="center" xs={12} sm={12} md={6}> <FormikField name="phoneNumberExtension" label={ @@ -1038,7 +1052,7 @@ export default function EditCreateUserProfile(props) { onKeyUpErrorMessage="" /> </Grid> - <Grid item align="center" xs={12} sm={12} md={6}> + <Grid item alignSelf="center" xs={12} sm={12} md={6}> <FormikField name="mobileNumber" label={i18next.t("mobile", "Mobile") as string} diff --git a/jams-react-client/src/views/UserProfile/cropImage.tsx b/jams-react-client/src/views/UserProfile/cropImage.tsx index 1bd5b8de102cf0c9c19aa3df2ca752ff6a87b300..f626794b5b1ea362a3967b50320a951d9b3bbc6f 100644 --- a/jams-react-client/src/views/UserProfile/cropImage.tsx +++ b/jams-react-client/src/views/UserProfile/cropImage.tsx @@ -1,4 +1,4 @@ -const createImage = (url) => +const createImage = (url: string): Promise<HTMLImageElement> => new Promise((resolve, reject) => { const image = new Image(); image.addEventListener("load", () => resolve(image)); @@ -7,7 +7,7 @@ const createImage = (url) => image.src = url; }); -function getRadianAngle(degreeValue) { +function getRadianAngle(degreeValue: number) { return (degreeValue * Math.PI) / 180; } diff --git a/jams-react-client/src/views/Users/Users.tsx b/jams-react-client/src/views/Users/Users.tsx index 301e58ee6633df295208c1bf57c95fd79c2dd5c2..1b3997b4f642ed1f780182436f48d2ebd67ccc02 100644 --- a/jams-react-client/src/views/Users/Users.tsx +++ b/jams-react-client/src/views/Users/Users.tsx @@ -120,7 +120,7 @@ export default function Users() { }; }, [history]); - const searchUsers = (value, page = "1") => { + const searchUsers = (value, page = 1) => { setSelectedPage(page); setLoading(true); setNoMatchFound(false);