From 9a3c6eb6dc14d22a99d9b44d74a538f3b698963b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Banno-Cloutier?= <leo.banno-cloutier@savoirfairelinux.com> Date: Wed, 9 Aug 2023 09:51:36 -0400 Subject: [PATCH] jams-react-client: add ServerUiCustomization Change-Id: I28af17ddcd0743152d72507e472b2f76c9b26d96 --- jams-react-client/package.json | 6 +-- .../CustomUiPreview/CustomUiPreview.tsx | 22 +++++++---- .../components/CustomUiPreview/JamiIdCard.tsx | 15 ++++++-- .../src/components/CustomUiPreview/TipBox.tsx | 11 ++++-- .../src/components/CustomUiPreview/luma.tsx | 2 +- .../components/Snackbar/BlueprintSnackbar.tsx | 19 ++++++---- .../src/routes/BlueprintRoute.tsx | 2 +- .../src/views/Blueprint/Blueprint.tsx | 29 ++++++++------- .../Blueprint/EditBlueprintConfiguration.tsx | 2 +- .../Blueprint/EditBlueprintPermissions.tsx | 2 +- .../src/views/Blueprint/EditBlueprintUi.tsx | 8 ++-- .../src/views/Blueprint/PolicyDataContext.tsx | 37 ++++++++++++++++--- .../views/Blueprint/ServerUiCustomization.tsx | 26 +++++++++++++ .../src/views/Blueprint/parsePolicyData.tsx | 8 ++-- .../views/Blueprint/policyData.constants.tsx | 4 ++ .../src/views/Blueprint/updatePolicyData.tsx | 35 +++++++++++------- 16 files changed, 158 insertions(+), 70 deletions(-) create mode 100644 jams-react-client/src/views/Blueprint/ServerUiCustomization.tsx diff --git a/jams-react-client/package.json b/jams-react-client/package.json index fb6bc962..62dce8f5 100644 --- a/jams-react-client/package.json +++ b/jams-react-client/package.json @@ -46,9 +46,9 @@ "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject", "install:clean": "rm -rf node_modules/ && rm -rf package-lock.json && npm install && npm start", - "format": "prettier --write --loglevel warn 'src/**/*.js'", - "lint:check": "eslint . --ext=js,jsx; exit 0", - "lint:fix": "eslint . --ext=js,jsx --fix; exit 0", + "format": "prettier --write --loglevel warn 'src/**/*.{js,jsx,ts,tsx}'", + "lint:check": "eslint . --ext=js,jsx,ts,tsx; exit 0", + "lint:fix": "eslint . --ext=js,jsx,ts,tsx --fix; exit 0", "build-package-css": "cp src/assets/css/material-dashboard-react.css dist/material-dashboard-react.css", "build-package": "npm run build-package-css && babel src --out-dir dist" }, diff --git a/jams-react-client/src/components/CustomUiPreview/CustomUiPreview.tsx b/jams-react-client/src/components/CustomUiPreview/CustomUiPreview.tsx index cc339aa2..598aa8af 100644 --- a/jams-react-client/src/components/CustomUiPreview/CustomUiPreview.tsx +++ b/jams-react-client/src/components/CustomUiPreview/CustomUiPreview.tsx @@ -1,16 +1,16 @@ -import React from "react"; +import React, { FC } from "react"; import { makeStyles } from "@mui/styles"; import backgroundImage from "assets/img/BG-ID_Jami.png"; import logoImage from "assets/img/favicon_jami.png"; -import TipBox from "./TipBox"; -import JamiIdCard from "./JamiIdCard"; +import { TipBox } from "./TipBox"; +import { JamiIdCard } from "./JamiIdCard"; import i18next from "i18next"; -import { url_path } from "../../globalUrls"; -import { url_port } from "../../globalUrls"; +import { url_path, url_port } from "../../globalUrls"; import { luma } from "./luma"; +import { UiCustomization } from "views/Blueprint/policyData.constants"; const styles = { root: { @@ -102,11 +102,17 @@ const styles = { const useStyles = makeStyles(styles); -export default function CustomUiPreview({ +interface CustomUiPreviewProps { + isOldPreview: boolean; + opacity: number; + uiCustomization: UiCustomization; +} + +export const CustomUiPreview: FC<CustomUiPreviewProps> = ({ isOldPreview, opacity, uiCustomization, -}) { +}) => { let { hasTitle, title, @@ -199,4 +205,4 @@ export default function CustomUiPreview({ </div> </div> ); -} +}; diff --git a/jams-react-client/src/components/CustomUiPreview/JamiIdCard.tsx b/jams-react-client/src/components/CustomUiPreview/JamiIdCard.tsx index 0b13fee7..67e92474 100644 --- a/jams-react-client/src/components/CustomUiPreview/JamiIdCard.tsx +++ b/jams-react-client/src/components/CustomUiPreview/JamiIdCard.tsx @@ -1,11 +1,10 @@ -import React from "react"; +import React, { FC } from "react"; import { makeStyles } from "@mui/styles"; import jamiIdImage from "assets/img/jami_id.svg"; import copySvg from "assets/img/BTN_Copy.svg"; import shareSvg from "assets/img/BTN_Share.svg"; -import { infoColor } from "assets/jss/material-dashboard-react"; import { luma } from "./luma"; const styles = { @@ -59,7 +58,15 @@ const styles = { const useStyles = makeStyles(styles); -export default function JamiIdCard({ color, isCompactDisplay }) { +interface JamiIdCardProps { + color: string; + isCompactDisplay: boolean; +} + +export const JamiIdCard: FC<JamiIdCardProps> = ({ + color, + isCompactDisplay, +}) => { const classes = useStyles({ color, isCompactDisplay }); return ( @@ -74,4 +81,4 @@ export default function JamiIdCard({ color, isCompactDisplay }) { </div> </div> ); -} +}; diff --git a/jams-react-client/src/components/CustomUiPreview/TipBox.tsx b/jams-react-client/src/components/CustomUiPreview/TipBox.tsx index d3961cee..bd6061c1 100644 --- a/jams-react-client/src/components/CustomUiPreview/TipBox.tsx +++ b/jams-react-client/src/components/CustomUiPreview/TipBox.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { FC } from "react"; import { makeStyles } from "@mui/styles"; import tipLightBulbImage from "assets/img/tip_light_bulb.svg"; @@ -30,7 +30,12 @@ const styles = { const useStyles = makeStyles(styles); -export default function TipBox({ color, text }) { +interface TipBoxProps { + color: string; + text: string; +} + +export const TipBox: FC<TipBoxProps> = ({ color, text }) => { const fontColor = luma(color) ? "#ffffff" : "#000000"; const classes = useStyles({ color, fontColor }); @@ -42,4 +47,4 @@ export default function TipBox({ color, text }) { <p className={classes.text}>{text}</p> </div> ); -} +}; diff --git a/jams-react-client/src/components/CustomUiPreview/luma.tsx b/jams-react-client/src/components/CustomUiPreview/luma.tsx index f91d3e22..34de2d09 100644 --- a/jams-react-client/src/components/CustomUiPreview/luma.tsx +++ b/jams-react-client/src/components/CustomUiPreview/luma.tsx @@ -1,4 +1,4 @@ -export const luma = (color) => { +export const luma = (color: string) => { const r = parseInt(color.slice(1, 3), 16); const g = parseInt(color.slice(3, 5), 16); const b = parseInt(color.slice(5, 7), 16); diff --git a/jams-react-client/src/components/Snackbar/BlueprintSnackbar.tsx b/jams-react-client/src/components/Snackbar/BlueprintSnackbar.tsx index 35b586fd..454d521e 100644 --- a/jams-react-client/src/components/Snackbar/BlueprintSnackbar.tsx +++ b/jams-react-client/src/components/Snackbar/BlueprintSnackbar.tsx @@ -1,9 +1,9 @@ -import React from "react"; -import PropTypes from "prop-types"; +import React, { Dispatch, FC, SetStateAction } from "react"; import MuiAlert from "@mui/material/Alert"; import Snackbar from "@mui/material/Snackbar"; import Slide from "@mui/material/Slide"; +import { SnackbarProps } from "views/Blueprint/PolicyDataContext"; // https://stackoverflow.com/a/67961603 const Alert = React.forwardRef(function Alert(props, ref) { @@ -14,7 +14,15 @@ const SlideTransition = React.forwardRef(function SlideTransition(props, ref) { return <Slide ref={ref} {...props} direction="left" />; }); -export default function BlueprintSnackbar({ snackbar, setSnackbar }) { +interface BlueprintSnackbarProps { + snackbar: SnackbarProps; + setSnackbar: Dispatch<SetStateAction<SnackbarProps>>; +} + +export const BlueprintSnackbar: FC<BlueprintSnackbarProps> = ({ + snackbar, + setSnackbar, +}) => { const snackbarRef = React.createRef(null); const handleClose = () => { @@ -36,9 +44,4 @@ export default function BlueprintSnackbar({ snackbar, setSnackbar }) { </Alert> </Snackbar> ); -} - -BlueprintSnackbar.propTypes = { - snackbar: PropTypes.object, - setSnackbar: PropTypes.func, }; diff --git a/jams-react-client/src/routes/BlueprintRoute.tsx b/jams-react-client/src/routes/BlueprintRoute.tsx index cc9834d9..33e3394b 100644 --- a/jams-react-client/src/routes/BlueprintRoute.tsx +++ b/jams-react-client/src/routes/BlueprintRoute.tsx @@ -1,7 +1,7 @@ import React from "react"; import BaseLayout from "layouts/BaseLayout"; -import Blueprint from "views/Blueprint/Blueprint"; +import { Blueprint } from "views/Blueprint/Blueprint"; export default function BlueprintRoute(props) { return ( diff --git a/jams-react-client/src/views/Blueprint/Blueprint.tsx b/jams-react-client/src/views/Blueprint/Blueprint.tsx index 441a2f43..d8d64f0b 100644 --- a/jams-react-client/src/views/Blueprint/Blueprint.tsx +++ b/jams-react-client/src/views/Blueprint/Blueprint.tsx @@ -1,6 +1,5 @@ -import React, { useState } from "react"; +import React, { FC, useState } from "react"; -import PropTypes from "prop-types"; import AppBar from "@mui/material/AppBar"; import Tabs from "@mui/material/Tabs"; import Tab from "@mui/material/Tab"; @@ -14,9 +13,13 @@ import { PolicyDataContextProvider } from "./PolicyDataContext"; import i18next from "i18next"; -function TabPanel(props) { - const { children, value, index, ...other } = props; +interface TabPanelProps { + children?: React.ReactNode; + index: number; + value: number; +} +const TabPanel: FC<TabPanelProps> = ({ children, value, index, ...other }) => { return ( <div role="tabpanel" @@ -32,15 +35,9 @@ function TabPanel(props) { )} </div> ); -} - -TabPanel.propTypes = { - children: PropTypes.node, - index: PropTypes.any.isRequired, - value: PropTypes.any.isRequired, }; -const a11yProps = (value, index) => { +const a11yProps = (value: number, index: number) => { return { id: `simple-tab-${index}`, "aria-controls": `simple-tabpanel-${index}`, @@ -48,10 +45,14 @@ const a11yProps = (value, index) => { }; }; -export default function Blueprint({ blueprintName }) { +interface BlueprintProps { + blueprintName: string; +} + +export const Blueprint: FC<BlueprintProps> = ({ blueprintName }) => { const [openedTab, setOpenedTab] = useState(0); - const handleChange = (event, newValue) => { + const handleChange = (event, newValue: number) => { setOpenedTab(newValue); }; @@ -93,4 +94,4 @@ export default function Blueprint({ blueprintName }) { </PolicyDataContextProvider> </div> ); -} +}; diff --git a/jams-react-client/src/views/Blueprint/EditBlueprintConfiguration.tsx b/jams-react-client/src/views/Blueprint/EditBlueprintConfiguration.tsx index 2fc0d60c..869dc68c 100644 --- a/jams-react-client/src/views/Blueprint/EditBlueprintConfiguration.tsx +++ b/jams-react-client/src/views/Blueprint/EditBlueprintConfiguration.tsx @@ -38,7 +38,7 @@ import i18next from "i18next"; import dashboardStyle from "assets/jss/material-dashboard-react/views/dashboardStyle"; import { hexToRgb, blackColor } from "assets/jss/material-dashboard-react"; -import BlueprintSnackbar from "components/Snackbar/BlueprintSnackbar"; +import { BlueprintSnackbar } from "components/Snackbar/BlueprintSnackbar"; import CustomPopupState from "components/CustomPopupState/CustomPopupState"; import { PolicyDataContext } from "./PolicyDataContext"; diff --git a/jams-react-client/src/views/Blueprint/EditBlueprintPermissions.tsx b/jams-react-client/src/views/Blueprint/EditBlueprintPermissions.tsx index 9ab64a98..0b0b0632 100644 --- a/jams-react-client/src/views/Blueprint/EditBlueprintPermissions.tsx +++ b/jams-react-client/src/views/Blueprint/EditBlueprintPermissions.tsx @@ -22,7 +22,7 @@ import TableCell from "@mui/material/TableCell"; import AddCircleOutlineIcon from "@mui/icons-material/AddCircleOutline"; import PriorityHighOutlinedIcon from "@mui/icons-material/PriorityHighOutlined"; -import BlueprintSnackbar from "components/Snackbar/BlueprintSnackbar"; +import { BlueprintSnackbar } from "components/Snackbar/BlueprintSnackbar"; import Button from "components/CustomButtons/Button"; import Card from "components/Card/Card"; import CardBody from "components/Card/CardBody"; diff --git a/jams-react-client/src/views/Blueprint/EditBlueprintUi.tsx b/jams-react-client/src/views/Blueprint/EditBlueprintUi.tsx index 82c0b8a8..1d62ef66 100644 --- a/jams-react-client/src/views/Blueprint/EditBlueprintUi.tsx +++ b/jams-react-client/src/views/Blueprint/EditBlueprintUi.tsx @@ -4,7 +4,7 @@ import { makeStyles } from "@mui/styles"; import Grid from "@mui/material/Grid"; import SettingsIcon from "@mui/icons-material/Settings"; -import BlueprintSnackbar from "components/Snackbar/BlueprintSnackbar"; +import { BlueprintSnackbar } from "components/Snackbar/BlueprintSnackbar"; import Card from "components/Card/Card"; import CardHeader from "components/Card/CardHeader"; import CardIcon from "components/Card/CardIcon"; @@ -25,7 +25,7 @@ import dashboardStyle from "assets/jss/material-dashboard-react/views/dashboardS import i18next from "i18next"; -import CustomUiPreview from "components/CustomUiPreview/CustomUiPreview"; +import { CustomUiPreview } from "components/CustomUiPreview/CustomUiPreview"; import EditBlueprintUiForm from "./EditBlueprintUiForm"; import { DEFAULT_UI_CUSTOMIZATION } from "./policyData.constants"; import { PolicyDataContext } from "./PolicyDataContext"; @@ -109,7 +109,7 @@ export default function EditBlueprintUi({ blueprintName }) { const [oldUiCustomization, setOldUiCustomization] = useState(uiCustomization); const [opacity, setOpacity] = useState(0); - const handleUpdateUi = (field, value) => { + const handleUpdateUi = (field: string, value: any) => { let newUiCustomization; if (typeof field === "object") { @@ -146,7 +146,7 @@ export default function EditBlueprintUi({ blueprintName }) { updatePolicyData("uiCustomization", newUiCustomization); }; - const handleImgDrop = (acceptedFiles, imgType) => { + const handleImgDrop = (acceptedFiles: string[], imgType: string) => { const formData = new FormData(); formData.append("file", acceptedFiles[0]); diff --git a/jams-react-client/src/views/Blueprint/PolicyDataContext.tsx b/jams-react-client/src/views/Blueprint/PolicyDataContext.tsx index 131db22a..3a642e6f 100644 --- a/jams-react-client/src/views/Blueprint/PolicyDataContext.tsx +++ b/jams-react-client/src/views/Blueprint/PolicyDataContext.tsx @@ -1,4 +1,10 @@ -import React, { createContext, useEffect, useState } from "react"; +import React, { + FC, + ReactNode, + createContext, + useEffect, + useState, +} from "react"; import axios from "axios"; @@ -6,11 +12,32 @@ import configApiCall from "../../api"; import { api_path_blueprints } from "../../globalUrls"; import { parsePolicyData } from "./parsePolicyData"; import { _updatePolicyData } from "./updatePolicyData"; -import { DEFAULT_POLICY_DATA } from "./policyData.constants"; +import { DEFAULT_POLICY_DATA, PolicyData } from "./policyData.constants"; -export const PolicyDataContext = createContext(null); +export interface SnackbarProps { + open: boolean; + severity: "success" | "error"; + message: string; +} -export const PolicyDataContextProvider = ({ blueprintName, children }) => { +interface PolicyDataProp { + policyData: PolicyData; + updatePolicyData: (field: string, value: string) => void; + snackbar: SnackbarProps; + setSnackbar: (snackbar: SnackbarProps) => void; +} + +export const PolicyDataContext = createContext<PolicyDataProp>(null); + +interface Props { + blueprintName: string; + children: ReactNode; +} + +export const PolicyDataContextProvider: FC<Props> = ({ + blueprintName, + children, +}) => { const [policyData, setPolicyData] = useState(DEFAULT_POLICY_DATA); const [snackbar, setSnackbar] = useState({ open: false, @@ -40,7 +67,7 @@ export const PolicyDataContextProvider = ({ blueprintName, children }) => { }); }, [blueprintName]); - const updatePolicyData = (field, value) => { + const updatePolicyData = (field: string, value: string) => { _updatePolicyData( blueprintName, policyData, diff --git a/jams-react-client/src/views/Blueprint/ServerUiCustomization.tsx b/jams-react-client/src/views/Blueprint/ServerUiCustomization.tsx new file mode 100644 index 00000000..c7271418 --- /dev/null +++ b/jams-react-client/src/views/Blueprint/ServerUiCustomization.tsx @@ -0,0 +1,26 @@ +export interface ServerUiCustomization { + /** if the title is undefined, we show the default title */ + title?: string; + /** if the description is undefined, we show the default description */ + description?: string; + areTipsEnabled: boolean; + + backgroundType: "color" | "image" | "default"; + /** only defined if backgroundType !== "default" */ + backgroundColorOrUrl?: string; + /** + * if this field is undefined, we show the default value + * The color is in argb format + */ + tipBoxAndIdColor?: string; + + /** + * if this field is undefined, we show the default value + * The color is in argb format + */ + mainBoxColor?: string; + + logoUrl?: string; + /** The logo size from 0 to 100. 100% if left undefined */ + logoSize?: number; +} diff --git a/jams-react-client/src/views/Blueprint/parsePolicyData.tsx b/jams-react-client/src/views/Blueprint/parsePolicyData.tsx index 25b5e1a7..dde06e90 100644 --- a/jams-react-client/src/views/Blueprint/parsePolicyData.tsx +++ b/jams-react-client/src/views/Blueprint/parsePolicyData.tsx @@ -5,9 +5,9 @@ import { api_path_get_ns_name_from_addr, api_path_get_user_profile, } from "../../globalUrls"; -import { DEFAULT_UI_CUSTOMIZATION } from "./policyData.constants"; +import { DEFAULT_UI_CUSTOMIZATION, PolicyData } from "./policyData.constants"; -const getModerators = async (defaultModerators) => { +const getModerators = async (defaultModerators: string) => { const moderators = []; const ids = defaultModerators.split("/").filter((id) => id !== ""); @@ -60,7 +60,7 @@ const setConfigurationSettings = (policyData) => { return policyData; }; -const convertArgbToRgba = (argb) => { +const convertArgbToRgba = (argb: string) => { if (argb.length === 7) { return argb; } @@ -114,7 +114,7 @@ const setCustomizationSettings = (policyData) => { return policyData; }; -export const parsePolicyData = async (data) => { +export const parsePolicyData = async (data: string): Promise<PolicyData> => { let policyData = JSON.parse(data); policyData = await setPermissionsSettings(policyData); diff --git a/jams-react-client/src/views/Blueprint/policyData.constants.tsx b/jams-react-client/src/views/Blueprint/policyData.constants.tsx index e90b9b0b..9e204d59 100644 --- a/jams-react-client/src/views/Blueprint/policyData.constants.tsx +++ b/jams-react-client/src/views/Blueprint/policyData.constants.tsx @@ -19,6 +19,8 @@ export const DEFAULT_UI_CUSTOMIZATION = { logoSize: 100, }; +export type UiCustomization = typeof DEFAULT_UI_CUSTOMIZATION; + export const DEFAULT_POLICY_DATA = { videoEnabled: true, publicInCalls: false, @@ -42,3 +44,5 @@ export const DEFAULT_POLICY_DATA = { uiCustomization: DEFAULT_UI_CUSTOMIZATION, }; + +export type PolicyData = typeof DEFAULT_POLICY_DATA; diff --git a/jams-react-client/src/views/Blueprint/updatePolicyData.tsx b/jams-react-client/src/views/Blueprint/updatePolicyData.tsx index 6b6368bf..85df6b6a 100644 --- a/jams-react-client/src/views/Blueprint/updatePolicyData.tsx +++ b/jams-react-client/src/views/Blueprint/updatePolicyData.tsx @@ -4,9 +4,12 @@ import i18next from "i18next"; import configApiCall from "api"; import { api_path_blueprints } from "globalUrls"; import { debounce } from "lodash"; -import { DEFAULT_UI_CUSTOMIZATION } from "./policyData.constants"; +import { DEFAULT_UI_CUSTOMIZATION, PolicyData } from "./policyData.constants"; +import { Dispatch, SetStateAction } from "react"; +import { SnackbarProps } from "./PolicyDataContext"; +import { ServerUiCustomization } from "./ServerUiCustomization"; -const updatePerms = (data, field, value) => { +const updatePerms = (data, field: string, value: any) => { data.defaultModerators = data.blueprintModerators .map((moderator) => moderator.id) .join("/"); @@ -51,7 +54,7 @@ const updateConfig = (data) => { return data; }; -const convertRgbaToArgb = (rgba) => { +const convertRgbaToArgb = (rgba: string) => { if (rgba.length === 7) { return rgba; } @@ -59,7 +62,7 @@ const convertRgbaToArgb = (rgba) => { throw new Error(`Invalid rgba value: "${rgba}}"`); } - return "#" + rgba.substr(7, 2) + rgba.substr(1, 6); + return "#" + rgba.substring(7, 9) + rgba.substring(1, 7); }; const updateUiCustomization = (data) => { @@ -135,7 +138,11 @@ const updateUiCustomization = (data) => { return data; }; -const sendPutRequest = (blueprintName, data, setSnackbar) => { +const sendPutRequest = ( + blueprintName: string, + data: ServerUiCustomization, + setSnackbar: (snackbar: SnackbarProps) => void +) => { console.log("PUT", data); axios( @@ -175,21 +182,23 @@ const debouncedSendPutRequest = debounce(sendPutRequest, 200, { }); export const _updatePolicyData = ( - blueprintName, - policyData, - setPolicyData, - field, - value, - setSnackbar + blueprintName: string, + policyData: PolicyData, + setPolicyData: Dispatch<SetStateAction<PolicyData>>, + field: string, + value: string, + setSnackbar: (snackbar: any) => void ) => { setPolicyData((state) => ({ ...state, [field]: value })); - let data = { ...policyData, [field]: value }; + let data: any = { ...policyData, [field]: value }; data = updatePerms(data, field, value); data = updateConfig(data); data = updateUiCustomization(data); + const ui: ServerUiCustomization = data; + console.log("updatePolicyData", { field, value }); - debouncedSendPutRequest(blueprintName, data, setSnackbar); + debouncedSendPutRequest(blueprintName, ui, setSnackbar); }; -- GitLab