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