Browse Source

chore: use styled component instead of makeStyles (#784)

* use styled for EmptyCard

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* usestyled for CustomButton

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* use styled for customDialog

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* use styled for customInput

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* use styled for customSelector

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* use styled for customSnackBar

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* remove CustomSwitch

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* use styled for CustomTooltip

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* use styled for DataListView

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* used styled for grid

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* use styled for layout

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* use styled for status

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* use styled uploader

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

---------

Signed-off-by: ryjiang <jiangruiyi@gmail.com>
ryjiang 4 months ago
parent
commit
23d51bbd11
34 changed files with 809 additions and 1175 deletions
  1. 61 66
      client/src/components/DataListView/DataListView.tsx
  2. 16 27
      client/src/components/cards/EmptyCard.tsx
  3. 0 1
      client/src/components/cards/Types.ts
  4. 29 102
      client/src/components/customButton/CustomButton.tsx
  5. 34 53
      client/src/components/customDialog/CustomDialog.tsx
  6. 24 34
      client/src/components/customDialog/CustomDialogTitle.tsx
  7. 94 119
      client/src/components/customDialog/DeleteDialogTemplate.tsx
  8. 51 44
      client/src/components/customDialog/DialogTemplate.tsx
  9. 16 36
      client/src/components/customInput/CustomInput.tsx
  10. 79 83
      client/src/components/customInput/SearchInput.tsx
  11. 32 41
      client/src/components/customSelector/CustomGroupedSelect.tsx
  12. 10 12
      client/src/components/customSelector/CustomMultiSelector.tsx
  13. 21 32
      client/src/components/customSnackBar/CustomSnackBar.tsx
  14. 0 36
      client/src/components/customSwitch/CustomSwitch.tsx
  15. 0 3
      client/src/components/customSwitch/Types.ts
  16. 5 7
      client/src/components/customToolTip/CustomToolTip.tsx
  17. 27 58
      client/src/components/grid/ActionBar.tsx
  18. 8 27
      client/src/components/grid/Grid.tsx
  19. 2 2
      client/src/components/grid/LoadingTable.tsx
  20. 1 2
      client/src/components/grid/Table.tsx
  21. 10 20
      client/src/components/grid/TableEditableHead.tsx
  22. 38 41
      client/src/components/grid/TableHead.tsx
  23. 26 32
      client/src/components/grid/TablePaginationActions.tsx
  24. 0 58
      client/src/components/grid/TableSwitch.tsx
  25. 0 14
      client/src/components/grid/ToolBar.tsx
  26. 0 2
      client/src/components/grid/Types.ts
  27. 93 103
      client/src/components/layout/Header.tsx
  28. 30 35
      client/src/components/layout/Wrapper.tsx
  29. 41 48
      client/src/components/status/Status.tsx
  30. 12 23
      client/src/components/status/StatusIcon.tsx
  31. 1 8
      client/src/components/uploader/Uploader.tsx
  32. 1 0
      client/src/i18n/cn/user.ts
  33. 1 0
      client/src/i18n/en/user.ts
  34. 46 6
      client/src/styles/theme.ts

+ 61 - 66
client/src/components/DataListView/DataListView.tsx

@@ -1,71 +1,71 @@
 import Typography from '@mui/material/Typography';
 import Chip from '@mui/material/Chip';
-import { makeStyles } from '@mui/styles';
+import { styled } from '@mui/material/styles';
 import { formatFieldType } from '@/utils';
 import DataView from '@/components/DataView/DataView';
 import { DYNAMIC_FIELD } from '@/consts';
 import CopyButton from '@/components/advancedSearch/CopyButton';
-import type { Theme } from '@mui/material/styles';
 import type { CollectionFullObject } from '@server/types';
 
 interface DataListViewProps {
   collection: CollectionFullObject;
   data: any;
 }
-const useStyles = makeStyles((theme: Theme) => ({
-  root: {
-    padding: 16,
-    cursor: 'initial',
-  },
-  dataTitleContainer: {
-    display: 'flex',
-    justifyContent: 'space-between',
-  },
-  title: {
-    fontSize: 14,
-    fontWeight: 600,
-  },
-  type: {
-    color: theme.palette.text.secondary,
-    marginLeft: 4,
-    marginTop: 2,
-  },
-  dataContainer: {
-    display: 'flex',
-    padding: 8,
-    border: `1px solid ${theme.palette.divider}`,
-    backgroundColor: theme.palette.background.paper,
-    borderRadius: 4,
-    marginBottom: 16,
-    maxHeight: 400,
-    overflow: 'auto',
-  },
-  copy: {
-    marginLeft: 0,
-    '& svg': {
-      width: 15,
-    },
-  },
-  dataTypeChip: {
-    fontSize: 11,
-    color: theme.palette.text.primary,
-    cursor: 'normal',
-    marginRight: 4,
-    marginLeft: 4,
-    backgroundColor: theme.palette.background.grey,
+
+// Styled components
+const Root = styled('div')(({ theme }) => ({
+  padding: 16,
+  cursor: 'initial',
+}));
+
+const DataTitleContainer = styled('div')({
+  display: 'flex',
+  justifyContent: 'space-between',
+});
+
+const Title = styled('span')({
+  fontSize: 14,
+  fontWeight: 600,
+});
+
+const Type = styled('span')(({ theme }) => ({
+  color: theme.palette.text.secondary,
+  marginLeft: 4,
+  marginTop: 2,
+}));
+
+const DataContainer = styled('div')(({ theme }) => ({
+  display: 'flex',
+  padding: 8,
+  border: `1px solid ${theme.palette.divider}`,
+  backgroundColor: theme.palette.background.paper,
+  borderRadius: 4,
+  marginBottom: 16,
+  maxHeight: 400,
+  overflow: 'auto',
+}));
+
+const StyledCopyButton = styled(CopyButton)({
+  marginLeft: 0,
+  '& svg': {
+    width: 15,
   },
+});
+
+const DataTypeChip = styled(Chip)(({ theme }) => ({
+  fontSize: 11,
+  color: theme.palette.text.primary,
+  cursor: 'normal',
+  marginRight: 4,
+  marginLeft: 4,
+  backgroundColor: theme.palette.background.grey,
 }));
 
 const DataListView = (props: DataListViewProps) => {
-  // props
   const { collection, data } = props;
-  // styles
-  const classes = useStyles();
 
-  // page data
+  // Merge dynamic fields into row
   let row = data[0];
-
-  // merge dymaic fields into row
   row = {
     ...row,
     ...row[DYNAMIC_FIELD],
@@ -80,40 +80,35 @@ const DataListView = (props: DataListViewProps) => {
   }
 
   return (
-    <div className={classes.root}>
+    <Root>
       {Object.keys(row).map((name: string, index: number) => {
         const field = collection.schema.fields.find(f => f.name === name);
         return (
           <div key={index}>
-            <div className={classes.dataTitleContainer}>
-              <span className={classes.title}>
+            <DataTitleContainer>
+              <Title>
                 {name}
-                <CopyButton
-                  className={classes.copy}
-                  value={row[name]}
-                  label={name}
-                />
-              </span>
-              <span className={classes.type}>
+                <StyledCopyButton value={row[name]} label={name} />
+              </Title>
+              <Type>
                 {field && (
-                  <Chip
-                    className={classes.dataTypeChip}
+                  <DataTypeChip
                     size="small"
                     label={formatFieldType(field) || 'meta'}
                   />
                 )}
-              </span>
-            </div>
-            <div className={classes.dataContainer}>
+              </Type>
+            </DataTitleContainer>
+            <DataContainer>
               <DataView
                 type={(field && field.data_type) || 'any'}
                 value={row[name]}
               />
-            </div>
+            </DataContainer>
           </div>
         );
       })}
-    </div>
+    </Root>
   );
 };
 

+ 16 - 27
client/src/components/cards/EmptyCard.tsx

@@ -1,49 +1,38 @@
+import { styled } from '@mui/material/styles';
 import Typography from '@mui/material/Typography';
 import CardContent from '@mui/material/CardContent';
-import { makeStyles } from '@mui/styles';
 import StatusIcon, { LoadingType } from '@/components/status/StatusIcon';
 import type { FC } from 'react';
-import type { Theme } from '@mui/material/styles';
 import type { EmptyCardProps } from './Types';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  wrapper: {
-    color: theme.palette.text.primary,
-    backgroundColor: theme.palette.background.paper,
-    flexDirection: 'column',
-    textAlign: 'center',
-  },
-  text: {
-    marginTop: theme.spacing(2),
-    fontSize: '36px',
-    lineHeight: '42px',
-    fontWeight: 'bold',
-    letterSpacing: '-0.02em',
-  },
-  subText: {
-    fontSize: '18px',
-    marginTop: theme.spacing(1),
-  },
+const StyledSection = styled('section')(({ theme }) => ({
+  color: theme.palette.text.disabled,
+  backgroundColor: theme.palette.background.paper,
+  flexDirection: 'column',
+  textAlign: 'center',
+  display: 'flex',
+  alignItems: 'center',
+  justifyContent: 'center',
+}));
+
+const TitleTypography = styled(Typography)(({ theme }) => ({
+  marginTop: theme.spacing(2),
 }));
 
 const EmptyCard: FC<EmptyCardProps> = ({
   icon,
   text,
   wrapperClass = '',
-  subText = '',
   loading = false,
 }) => {
-  const classes = useStyles();
-
   return (
-    <section className={`flex-center ${classes.wrapper} ${wrapperClass}`}>
+    <StyledSection className={wrapperClass}>
       <CardContent>
         {loading && <StatusIcon type={LoadingType.CREATING} size={40} />}
         {icon}
-        <Typography className={classes.text}>{text}</Typography>
-        <Typography className={classes.subText}>{subText}</Typography>
+        <TitleTypography variant="h2">{text}</TitleTypography>
       </CardContent>
-    </section>
+    </StyledSection>
   );
 };
 

+ 0 - 1
client/src/components/cards/Types.ts

@@ -2,7 +2,6 @@ import { ReactElement } from 'react';
 
 export interface EmptyCardProps {
   text: string;
-  subText?: string;
   icon?: ReactElement;
   wrapperClass?: string;
   loading?: boolean;

+ 29 - 102
client/src/components/customButton/CustomButton.tsx

@@ -1,111 +1,38 @@
 import Button from '@mui/material/Button';
 import Tooltip from '@mui/material/Tooltip';
-import { makeStyles } from '@mui/styles';
 import type { ButtonProps } from '@mui/material/Button';
-import type { Theme } from '@mui/material/styles';
 
-const buttonStyle = makeStyles((theme: Theme) => ({
-  button: {
-    padding: theme.spacing(1, 3),
-    textTransform: 'initial',
-    fontWeight: 'bold',
-  },
-  textBtn: {
-    color: theme.palette.primary.main,
-    padding: theme.spacing(1),
+interface CustomButtonProps extends ButtonProps {
+  tooltip?: string;
+  tooltipPlacement?:
+    | 'bottom'
+    | 'left'
+    | 'right'
+    | 'top'
+    | 'bottom-end'
+    | 'bottom-start'
+    | 'left-end'
+    | 'left-start'
+    | 'right-end'
+    | 'right-start'
+    | 'top-end'
+    | 'top-start';
+}
 
-    '&:hover': {
-      backgroundColor: theme.palette.primary.main,
-      color: theme.palette.background.paper,
-    },
-  },
-  containedBtn: {
-    color: theme.palette.background.paper,
-    backgroundColor: theme.palette.primary.main,
-    boxShadow: 'initial',
-    fontWeight: 'bold',
-    lineHeight: '24px',
-    '&:hover': {
-      backgroundColor: theme.palette.primary.dark,
-      boxShadow: 'initial',
-    },
-  },
-  containedSecondary: {
-    backgroundColor: '#fc4c02',
+const CustomButton = ({
+  tooltip,
+  tooltipPlacement = 'top',
+  disabled,
+  ...props
+}: CustomButtonProps) => {
+  const button = <Button disabled={disabled} {...props} />;
 
-    '&:hover': {
-      backgroundColor: '#fc4c02',
-    },
-  },
-  disabledBtn: {
-    pointerEvents: 'none',
-  },
-}));
-
-// props types same as Material Button
-const CustomButton = (
-  props: ButtonProps & {
-    tooltip?: string;
-    tooltipPlacement?:
-      | 'bottom'
-      | 'left'
-      | 'right'
-      | 'top'
-      | 'bottom-end'
-      | 'bottom-start'
-      | 'left-end'
-      | 'left-start'
-      | 'right-end'
-      | 'right-start'
-      | 'top-end'
-      | 'top-start'
-      | undefined;
-  }
-) => {
-  const classes = buttonStyle();
-  const { tooltip, tooltipPlacement, disabled, ...otherProps } = props;
-
-  const btn = (
-    <Button
-      classes={{
-        root: classes.button,
-        text: classes.textBtn,
-        contained: classes.containedBtn,
-        containedSecondary: classes.containedSecondary,
-        disabled: classes.disabledBtn,
-      }}
-      disabled={disabled}
-      {...otherProps}
-    >
-      {props.children}
-    </Button>
-  );
-
-  return (
-    <>
-      {/*
-      add span to let disabled elements show tooltip
-      see https://material-ui.com/zh/components/tooltips/#disabled-elements
-      */}
-      {tooltip ? (
-        <Tooltip title={tooltip} placement={tooltipPlacement}>
-          {disabled ? <span>{btn}</span> : btn}
-        </Tooltip>
-      ) : (
-        <Button
-          classes={{
-            root: classes.button,
-            text: classes.textBtn,
-            contained: classes.containedBtn,
-            containedSecondary: classes.containedSecondary,
-          }}
-          disabled={disabled}
-          {...otherProps}
-        >
-          {props.children}
-        </Button>
-      )}
-    </>
+  return tooltip ? (
+    <Tooltip title={tooltip} placement={tooltipPlacement}>
+      {disabled ? <span>{button}</span> : button}
+    </Tooltip>
+  ) : (
+    button
   );
 };
 

+ 34 - 53
client/src/components/customDialog/CustomDialog.tsx

@@ -4,33 +4,34 @@ import DialogContent from '@mui/material/DialogContent';
 import Dialog from '@mui/material/Dialog';
 import Typography from '@mui/material/Typography';
 import { useTranslation } from 'react-i18next';
+import { styled } from '@mui/material/styles';
 import CustomButton from '../customButton/CustomButton';
 import CustomDialogTitle from './CustomDialogTitle';
-import { makeStyles } from '@mui/styles';
-import type { Theme } from '@mui/material/styles';
 import type { CustomDialogType } from './Types';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  paper: {
-    border: `1px solid ${theme.palette.divider}`,
-  },
-  noticePaper: {},
-  paperSm: {
-    maxWidth: '80%',
-  },
-  dialogContent: {
-    // marginTop: theme.spacing(2),
-  },
-  title: {},
-  cancel: {
-    // color: theme.palette.common.black,
-    // opacity: 0.4,
-  },
+const StyledDialog = styled(Dialog, {
+  shouldForwardProp: prop =>
+    !['type', 'containerClass'].includes(prop as string),
+})<{ type?: 'notice' | 'custom'; containerClass?: string }>(
+  ({ theme, type, containerClass }) => ({
+    '& .MuiDialog-paper': {
+      border: `1px solid ${theme.palette.divider}`,
+      ...(type !== 'notice' && { maxWidth: '80%' }),
+    },
+    ...(containerClass && { container: containerClass }),
+  })
+);
+
+const StyledDialogContent = styled(DialogContent)(({ theme }) => ({
+  /* If needed, add dialog content styles here */
+}));
+
+const StyledCancelButton = styled(CustomButton)(({ theme }) => ({
+  color: theme.palette.text.secondary,
 }));
 
 const CustomDialog: FC<CustomDialogType> = props => {
   const { t } = useTranslation('btn');
-  const classes = useStyles();
   const { open, type, params, onClose, containerClass = '' } = props;
   const {
     title,
@@ -41,14 +42,13 @@ const CustomDialog: FC<CustomDialogType> = props => {
     cancelLabel = t('cancel'),
     confirmClass = '',
     handleClose,
-  } = params; // for notice type
-  const { component: CustomComponent } = params; // for custom type
+  } = params;
+  const { component: CustomComponent } = params;
+
   const handleConfirm = async (event: React.FormEvent<HTMLFormElement>) => {
     if (confirm) {
       const res = await confirm();
-      if (!res) {
-        return;
-      }
+      if (!res) return;
     }
     handleClose ? handleClose() : onClose();
     event.preventDefault();
@@ -57,49 +57,30 @@ const CustomDialog: FC<CustomDialogType> = props => {
   const handleCancel = async () => {
     if (cancel) {
       const res = await cancel();
-      if (!res) {
-        return;
-      }
+      if (!res) return;
     }
     handleClose ? handleClose() : onClose();
   };
 
   return (
-    <Dialog
-      classes={{
-        paper: `${classes.paper} ${
-          type === 'notice' ? classes.noticePaper : ''
-        }`,
-        paperWidthSm: type === 'notice' ? '' : classes.paperSm,
-        container: `${containerClass}`,
-      }}
+    <StyledDialog
+      type={type}
+      containerClass={containerClass}
       open={open}
       onClose={(event, reason) => {
-        if (reason !== 'backdropClick') {
-          handleCancel();
-        }
+        if (reason !== 'backdropClick') handleCancel();
       }}
     >
       {type === 'notice' ? (
         <form onSubmit={handleConfirm}>
-          <CustomDialogTitle
-            classes={{ root: classes.title }}
-            onClose={handleCancel}
-          >
+          <CustomDialogTitle onClose={handleCancel}>
             <Typography variant="body1">{title}</Typography>
           </CustomDialogTitle>
-          {component && (
-            <DialogContent classes={{ root: classes.dialogContent }}>
-              {component}
-            </DialogContent>
-          )}
+          {component && <StyledDialogContent>{component}</StyledDialogContent>}
           <DialogActions>
-            <CustomButton
-              onClick={() => handleCancel()}
-              className={classes.cancel}
-            >
+            <StyledCancelButton onClick={handleCancel}>
               {cancelLabel}
-            </CustomButton>
+            </StyledCancelButton>
             <CustomButton
               type="submit"
               color="primary"
@@ -113,7 +94,7 @@ const CustomDialog: FC<CustomDialogType> = props => {
       ) : (
         CustomComponent
       )}
-    </Dialog>
+    </StyledDialog>
   );
 };
 

+ 24 - 34
client/src/components/customDialog/CustomDialogTitle.tsx

@@ -1,28 +1,27 @@
 import Typography from '@mui/material/Typography';
 import MuiDialogTitle from '@mui/material/DialogTitle';
 import icons from '../icons/Icons';
-import { makeStyles } from '@mui/styles';
+import { styled } from '@mui/material/styles';
 import type { DialogTitleProps } from '@mui/material/DialogTitle';
-import type { Theme } from '@mui/material/styles';
 
-const getStyles = makeStyles((theme: Theme) => ({
-  root: {
-    display: 'flex',
-    justifyContent: 'space-between',
-    alignItems: 'center',
-    marginBottom: 8,
-    paddingTop: 32,
-  },
-  title: {
-    fontWeight: 500,
-    wordBreak: 'break-all',
-    fontSize: '20px',
-  },
-  icon: {
-    fontSize: '18px',
-    color: theme.palette.text.primary,
-    cursor: 'pointer',
-  },
+const StyledDialogTitle = styled(MuiDialogTitle)(({ theme }) => ({
+  display: 'flex',
+  justifyContent: 'space-between',
+  alignItems: 'center',
+  marginBottom: theme.spacing(1),
+  paddingTop: theme.spacing(4),
+}));
+
+const TitleText = styled(Typography)(({ theme }) => ({
+  fontWeight: 500,
+  wordBreak: 'break-all',
+  fontSize: '20px',
+}));
+
+const CloseIcon = styled(icons.clear)(({ theme }) => ({
+  fontSize: '18px',
+  color: theme.palette.text.primary,
+  cursor: 'pointer',
 }));
 
 interface IProps extends DialogTitleProps {
@@ -38,24 +37,15 @@ const CustomDialogTitle = (props: IProps) => {
     showCloseIcon = true,
     ...other
   } = props;
-  const innerClass = getStyles();
-
-  const ClearIcon = icons.clear;
 
   return (
-    <MuiDialogTitle className={`${innerClass.root} ${classes.root}`} {...other}>
-      <Typography variant="body2" className={innerClass.title}>
-        {children}
-      </Typography>
+    <StyledDialogTitle className={classes.root} {...other}>
+      <TitleText variant="body2">{children}</TitleText>
       {showCloseIcon && onClose ? (
-        <ClearIcon
-          data-testid="clear-icon"
-          classes={{ root: innerClass.icon }}
-          onClick={onClose}
-        />
+        <CloseIcon data-testid="clear-icon" onClick={onClose} />
       ) : null}
-    </MuiDialogTitle>
+    </StyledDialogTitle>
   );
 };
 
-export default CustomDialogTitle;
+export default CustomDialogTitle;

+ 94 - 119
client/src/components/customDialog/DeleteDialogTemplate.tsx

@@ -1,165 +1,140 @@
-import DialogActions from '@mui/material/DialogActions';
-import DialogContent from '@mui/material/DialogContent';
-import TextField from '@mui/material/TextField';
-import Typography from '@mui/material/Typography';
-import Checkbox from '@mui/material/Checkbox';
-import FormControlLabel from '@mui/material/FormControlLabel';
-import { FC, useContext, useState } from 'react';
+import { FC, useContext, useState, ChangeEvent } from 'react';
 import { useTranslation } from 'react-i18next';
+import { styled } from '@mui/material/styles';
+import {
+  DialogActions,
+  DialogContent,
+  TextField,
+  Typography,
+  Checkbox,
+  FormControlLabel,
+} from '@mui/material';
 import CustomButton from '@/components/customButton/CustomButton';
 import CustomDialogTitle from '@/components/customDialog/CustomDialogTitle';
 import { rootContext } from '@/context';
-import { makeStyles } from '@mui/styles';
-import type { ChangeEvent } from 'react';
-import type { Theme } from '@mui/material/styles';
 import type { DeleteDialogContentType } from '@/components/customDialog/Types';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  root: {
-    maxWidth: 540,
-    backgroundColor: theme.palette.background.paper,
-  },
-  info: {
-    marginBottom: theme.spacing(0.5),
-    color: theme.palette.text.secondary,
-  },
-  mb: {
-    marginBottom: theme.spacing(2.5),
-  },
-  btnWrapper: {
-    display: 'flex',
-  },
-  label: {
-    display: 'none',
-  },
-  btnLabel: {
-    fontWeight: 'bold',
-  },
-  input: {
+const Root = styled('div')(({ theme }) => ({
+  maxWidth: 540,
+  backgroundColor: theme.palette.background.paper,
+}));
+
+const DialogSection = styled('div')(({ theme }) => ({
+  marginBottom: theme.spacing(2.5),
+}));
+
+const StyledDialogTitle = styled(CustomDialogTitle)(({ theme }) => ({
+  marginBottom: theme.spacing(2.5),
+}));
+
+const StyledTypography = styled(Typography)(({ theme }) => ({
+  marginBottom: theme.spacing(0.5),
+  color: theme.palette.text.secondary,
+}));
+
+const StyledTextField = styled(TextField)({
+  '& .MuiInputBase-input': {
     padding: '10px 12px',
   },
-  cancelBtn: {
-    color: theme.palette.text.secondary,
+  '& .MuiInputLabel-root': {
+    display: 'none',
   },
-  checkBox: {},
+});
+
+const ActionButtons = styled(DialogActions)({
+  display: 'flex',
+});
+
+const CancelButton = styled(CustomButton)(({ theme }) => ({
+  color: theme.palette.text.secondary,
 }));
 
-const DeleteTemplate: FC<DeleteDialogContentType> = props => {
-  const {
-    title,
-    text,
-    label,
-    compare,
-    handleDelete,
-    handleCancel,
-    forceDelLabel,
-  } = props;
+const BoldText = styled('strong')({
+  fontWeight: 'bold',
+});
+
+const DeleteTemplate: FC<DeleteDialogContentType> = ({
+  title,
+  text,
+  label,
+  compare,
+  handleDelete,
+  handleCancel,
+  forceDelLabel,
+}) => {
   const { handleCloseDialog } = useContext(rootContext);
-  const classes = useStyles();
   const { t: dialogTrans } = useTranslation('dialog');
   const { t: btnTrans } = useTranslation('btn');
 
-  const [value, setValue] = useState<string>('');
-  const [force, setForce] = useState<boolean>(false);
-  const [deleteReady, setDeleteReady] = useState<boolean>(false);
+  const [value, setValue] = useState('');
+  const [force, setForce] = useState(false);
+  const deleteReady = value.toLowerCase() === (compare || label).toLowerCase();
 
-  const onCancelClick = () => {
+  const handleCancelClick = () => {
     handleCloseDialog();
-    handleCancel && handleCancel();
-  };
-
-  const onDeleteClick = (event: React.FormEvent<HTMLFormElement>) => {
-    handleDelete(force);
-    event.preventDefault();
-  };
-
-  const onChange = (event: ChangeEvent<HTMLInputElement>) => {
-    const value = event.target.value;
-    setValue(value);
-
-    setDeleteReady(value.toLowerCase() === (compare || label).toLowerCase());
+    handleCancel?.();
   };
 
   return (
-    <div className={classes.root}>
-      <form onSubmit={onDeleteClick}>
-        <CustomDialogTitle
-          classes={{ root: classes.mb }}
-          onClose={onCancelClick}
-        >
+    <Root>
+      <form
+        onSubmit={e => {
+          e.preventDefault();
+          handleDelete(force);
+        }}
+      >
+        <StyledDialogTitle onClose={handleCancelClick}>
           {title}
-        </CustomDialogTitle>
+        </StyledDialogTitle>
 
         <DialogContent>
-          <Typography
-            variant="body1"
-            className={classes.info}
-            dangerouslySetInnerHTML={{ __html: text }}
-          ></Typography>
-          <Typography variant="body1" className={classes.mb}>
-            {dialogTrans('deleteTipAction')}
-            <strong className={classes.btnLabel}>{` ${(
-              compare || label
-            ).toLowerCase()} `}</strong>
-            {dialogTrans('deleteTipPurpose')}
-          </Typography>
-          <TextField
-            value={value}
-            onChange={onChange}
-            InputLabelProps={{
-              classes: {
-                root: classes.label,
-              },
-            }}
-            InputProps={{
-              classes: {
-                input: classes.input,
-              },
-            }}
+          <DialogSection>
+            <StyledTypography
+              variant="body1"
+              dangerouslySetInnerHTML={{ __html: text }}
+            />
+            <Typography variant="body1">
+              {dialogTrans('deleteTipAction')}
+              <BoldText>{` ${(compare || label).toLowerCase()} `}</BoldText>
+              {dialogTrans('deleteTipPurpose')}
+            </Typography>
+          </DialogSection>
+
+          <StyledTextField
+            fullWidth
             variant="filled"
-            fullWidth={true}
+            value={value}
+            onChange={(e: ChangeEvent<HTMLInputElement>) =>
+              setValue(e.target.value)
+            }
           />
-          {forceDelLabel ? (
+
+          {forceDelLabel && (
             <FormControlLabel
               control={
-                <Checkbox
-                  onChange={(
-                    e: React.ChangeEvent<HTMLInputElement>,
-                    checked: boolean
-                  ) => {
-                    setForce(checked);
-                  }}
-                />
+                <Checkbox onChange={(e, checked) => setForce(checked)} />
               }
-              key={'force'}
               label={forceDelLabel}
-              value={true}
               checked={force}
-              className={classes.checkBox}
             />
-          ) : null}
+          )}
         </DialogContent>
 
-        <DialogActions className={classes.btnWrapper}>
-          <CustomButton
-            name="cancel"
-            onClick={onCancelClick}
-            className={classes.cancelBtn}
-          >
+        <ActionButtons>
+          <CancelButton onClick={handleCancelClick}>
             {btnTrans('cancel')}
-          </CustomButton>
+          </CancelButton>
           <CustomButton
             type="submit"
             variant="contained"
             color="secondary"
             disabled={!deleteReady}
-            name="delete"
           >
             {label}
           </CustomButton>
-        </DialogActions>
+        </ActionButtons>
       </form>
-    </div>
+    </Root>
   );
 };
 

+ 51 - 44
client/src/components/customDialog/DialogTemplate.tsx

@@ -1,49 +1,54 @@
 import { FC, useRef, useState } from 'react';
 import { useTranslation } from 'react-i18next';
-import DialogContent from '@mui/material/DialogContent';
-import DialogActions from '@mui/material/DialogActions';
-import CircularProgress from '@mui/material/CircularProgress';
+import { styled } from '@mui/material/styles';
+import { DialogContent, DialogActions, CircularProgress } from '@mui/material';
 import CustomDialogTitle from './CustomDialogTitle';
 import CustomButton from '../customButton/CustomButton';
 import CodeView from '../code/CodeView';
-import { makeStyles } from '@mui/styles';
-import type { Theme } from '@mui/material/styles';
 import type { DialogContainerProps } from './Types';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  wrapper: {
+const Wrapper = styled('section')({
+  display: 'flex',
+  '& form': {
     display: 'flex',
-    '& form': {
-      display: 'flex',
-    },
-    '& .MuiDialogContent-root': {
-      maxHeight: '60vh',
-    },
   },
-  block: {
+  '& .MuiDialogContent-root': {
+    maxHeight: '60vh',
+  },
+});
+
+const DialogBlock = styled('div')<{ showCode: boolean }>(
+  ({ theme, showCode }) => ({
     color: theme.palette.text.primary,
     backgroundColor: theme.palette.background.paper,
-  },
-  dialog: {
     minWidth: 540,
-  },
-  codeWrapper: {
-    width: (props: { showCode: boolean }) => (props.showCode ? 540 : 0),
     transition: 'width 0.2s',
-  },
-  code: {
+    width: showCode ? 540 : 'auto',
+  })
+);
+
+const CodeWrapper = styled('div')<{ showCode: boolean }>(
+  ({ theme, showCode }) => ({
+    color: theme.palette.text.primary,
+    backgroundColor: theme.palette.background.paper,
+    width: showCode ? 540 : 0,
+    transition: 'width 0.2s',
+  })
+);
+
+const CodeContainer = styled('div')<{ showCode: boolean }>(
+  ({ theme, showCode }) => ({
     height: '100%',
-    // set code view padding 0 if not show
-    padding: (props: { showCode: boolean }) =>
-      props.showCode ? theme.spacing(4) : 0,
-  },
-  actions: {
-    paddingTop: theme.spacing(1),
-    paddingBottom: theme.spacing(2),
-    justifyContent: 'space-between',
-    '& .btn': {
-      margin: theme.spacing(0.5),
-    },
+    padding: showCode ? theme.spacing(4) : 0,
+  })
+);
+
+const Actions = styled(DialogActions)(({ theme }) => ({
+  paddingTop: theme.spacing(1),
+  paddingBottom: theme.spacing(2),
+  justifyContent: 'space-between',
+  '& .btn': {
+    margin: theme.spacing(0.5),
   },
 }));
 
@@ -60,7 +65,6 @@ const DialogTemplate: FC<DialogContainerProps> = ({
   showCancel = true,
   showCloseIcon = true,
   leftActions,
-  // needed for code mode
   showCode = false,
   codeBlocksData = [],
   dialogClass = '',
@@ -69,7 +73,6 @@ const DialogTemplate: FC<DialogContainerProps> = ({
   const { t } = useTranslation('btn');
   const cancel = cancelLabel || t('cancel');
   const confirm = confirmLabel || t('confirm');
-  const classes = useStyles({ showCode });
   const onCancel = handleCancel || handleClose;
 
   const dialogRef = useRef(null);
@@ -80,17 +83,19 @@ const DialogTemplate: FC<DialogContainerProps> = ({
     try {
       await handleConfirm();
     } catch (error) {
+      console.error(error);
     } finally {
       setConfirming(false);
     }
   };
 
   return (
-    <section className={classes.wrapper}>
+    <Wrapper>
       <form onSubmit={_handleConfirm}>
-        <div
+        <DialogBlock
+          showCode={showCode}
           ref={dialogRef}
-          className={`${classes.dialog} ${classes.block} ${dialogClass}`}
+          className={dialogClass}
         >
           <CustomDialogTitle
             onClose={handleClose}
@@ -100,7 +105,7 @@ const DialogTemplate: FC<DialogContainerProps> = ({
           </CustomDialogTitle>
           <DialogContent>{children}</DialogContent>
           {showActions && (
-            <DialogActions className={classes.actions}>
+            <Actions>
               <div>{leftActions}</div>
               <div>
                 {showCancel && (
@@ -124,17 +129,19 @@ const DialogTemplate: FC<DialogContainerProps> = ({
                   {confirming ? <CircularProgress size={16} /> : confirm}
                 </CustomButton>
               </div>
-            </DialogActions>
+            </Actions>
           )}
-        </div>
+        </DialogBlock>
 
-        <div className={`${classes.block} ${classes.codeWrapper}`}>
+        <CodeWrapper showCode={showCode}>
           {showCode && (
-            <CodeView wrapperClass={classes.code} data={codeBlocksData} />
+            <CodeContainer showCode={showCode}>
+              <CodeView data={codeBlocksData} />
+            </CodeContainer>
           )}
-        </div>
+        </CodeWrapper>
       </form>
-    </section>
+    </Wrapper>
   );
 };
 

+ 16 - 36
client/src/components/customInput/CustomInput.tsx

@@ -6,9 +6,9 @@ import Input from '@mui/material/Input';
 import InputAdornment from '@mui/material/InputAdornment';
 import InputLabel from '@mui/material/InputLabel';
 import TextField from '@mui/material/TextField';
-import { makeStyles } from '@mui/styles';
 import Icons from '../icons/Icons';
 import { ReactElement } from 'react';
+import { styled } from '@mui/material/styles';
 import type { FilledTextFieldProps } from '@mui/material/TextField';
 import type { StandardTextFieldProps } from '@mui/material/TextField';
 import type { Theme } from '@mui/material/styles';
@@ -69,10 +69,8 @@ const handleOnChange = (param: IChangeParam) => {
   }
 };
 
-const getAdornmentStyles = makeStyles((theme: Theme) => ({
-  icon: {
-    color: theme.palette.text.secondary,
-  },
+const StyledIconButton = styled(IconButton)(({ theme }: { theme: Theme }) => ({
+  color: theme.palette.text.secondary,
 }));
 
 const getAdornmentInput = (
@@ -93,8 +91,6 @@ const getAdornmentInput = (
     onInputChange,
   } = config;
 
-  const classes = getAdornmentStyles();
-
   const param = {
     cb: onInputBlur || (() => {}),
     validations: validations || [],
@@ -126,7 +122,7 @@ const getAdornmentInput = (
         }}
         endAdornment={
           <InputAdornment position="end">
-            <IconButton
+            <StyledIconButton
               onClick={onIconClick || (() => {})}
               edge="end"
               role="icon-button"
@@ -134,10 +130,10 @@ const getAdornmentInput = (
             >
               {isPasswordType
                 ? showPassword
-                  ? Icons.visible({ classes: { root: classes.icon } })
-                  : Icons.invisible({ classes: { root: classes.icon } })
+                  ? Icons.visible()
+                  : Icons.invisible()
                 : icon}
-            </IconButton>
+            </StyledIconButton>
           </InputAdornment>
         }
         inputProps={{
@@ -255,34 +251,18 @@ const getTextfield = (
   );
 };
 
-const getStyles = makeStyles((theme: Theme) => ({
-  errWrapper: {
-    display: 'flex',
-    alignItems: 'flex-start',
-    color: `${theme.palette.error.main}`,
-    wordWrap: 'break-word',
-    wordBreak: 'break-all',
-    overflow: 'hidden',
-    marginLeft: '12px',
-  },
-  errBtn: {
-    marginRight: `${theme.spacing(1)}`,
-  },
+const ErrWrapper = styled('span')(({ theme }: { theme: Theme }) => ({
+  display: 'flex',
+  alignItems: 'flex-start',
+  color: theme.palette.error.main,
+  wordWrap: 'break-word',
+  wordBreak: 'break-all',
+  overflow: 'hidden',
+  marginLeft: '12px',
 }));
 
 const createHelperTextNode = (hint: string): ReactElement => {
-  const classes = getStyles();
-  return (
-    <span className={classes.errWrapper}>
-      {/* {Icons.error({
-        fontSize: 'small',
-        classes: {
-          root: classes.errBtn,
-        },
-      })} */}
-      {hint}
-    </span>
-  );
+  return <ErrWrapper>{hint}</ErrWrapper>;
 };
 
 const CustomInput = (props: ICustomInputProps) => {

+ 79 - 83
client/src/components/customInput/SearchInput.tsx

@@ -1,51 +1,46 @@
 import InputAdornment from '@mui/material/InputAdornment';
 import TextField from '@mui/material/TextField';
-import { makeStyles } from '@mui/styles';
+import { styled } from '@mui/material/styles';
 import { useRef, FC, useState, useEffect, useMemo } from 'react';
 import { useTranslation } from 'react-i18next';
 import Icons from '../icons/Icons';
-import type { Theme } from '@mui/material/styles';
 import type { SearchType } from './Types';
 
-const useSearchStyles = makeStyles((theme: Theme) => ({
-  wrapper: {
-    display: 'flex',
+const SearchWrapper = styled('div')({
+  display: 'flex',
+});
+
+const StyledTextField = styled(TextField, {
+  shouldForwardProp: prop => prop !== 'searched',
+})<{ searched?: boolean }>(({ theme, searched }) => ({
+  backgroundColor: theme.palette.background.paper,
+  padding: theme.spacing(1),
+  width: '240px',
+  border: `1px solid ${theme.palette.divider}`,
+  fontSize: '14px',
+  transition: 'all 0.2s',
+
+  '& .MuiAutocomplete-endAdornment': {
+    right: theme.spacing(0.5),
   },
-  input: {
-    backgroundColor: theme.palette.background.paper,
-    padding: theme.spacing(1),
-    width: '240px',
-    border: `1px solid ${theme.palette.divider}`,
-    fontSize: '14px',
-
-    transition: 'all 0.2s',
 
-    '& .MuiAutocomplete-endAdornment': {
-      right: theme.spacing(0.5),
-    },
+  '& .MuiInput-underline:before': {
+    border: 'none',
+  },
 
-    '& .MuiInput-underline:before': {
-      border: 'none',
-    },
+  '& .MuiInput-underline:after': {
+    border: 'none',
+  },
 
-    '& .MuiInput-underline:after': {
-      border: 'none',
-    },
+  '&:focus-within': {
+    border: `1px solid ${theme.palette.primary.main}`,
 
-    /**
-     * when input focus
-     * 1. change parent wrapper border color
-     * 2. hide input start search icon
-     */
-    '&:focus-within': {
-      border: `1px solid ${theme.palette.primary.main}`,
-
-      '& $searchIcon': {
-        width: 0,
-      },
+    '& .search-icon': {
+      width: 0,
     },
   },
-  textfield: {
+
+  '& .MuiInputBase-input': {
     padding: 0,
     height: '16px',
 
@@ -53,34 +48,42 @@ const useSearchStyles = makeStyles((theme: Theme) => ({
       caretColor: theme.palette.primary.main,
     },
   },
-  searchIcon: {
+}));
+
+const SearchIconWrapper = styled('span')<{ searched?: boolean }>(
+  ({ theme, searched }) => ({
     color: theme.palette.text.secondary,
     cursor: 'pointer',
     fontSize: '20px',
-    width: (props: { searched: boolean }) => `${props.searched ? 0 : '20px'}`,
-
+    width: searched ? 0 : '20px',
     transition: 'width 0.2s',
-  },
-  clearIcon: {
-    color: theme.palette.primary.main,
-    cursor: 'pointer',
-  },
-  iconWrapper: {
-    opacity: (props: { searched: boolean }) => `${props.searched ? 1 : 0}`,
+    display: 'flex',
+    justifyContent: 'center',
+    alignItems: 'center',
+    opacity: searched ? 0 : 1,
+    overflow: 'hidden',
+  })
+);
+
+const IconWrapper = styled('span')<{ searched?: boolean }>(
+  ({ theme, searched }) => ({
+    opacity: searched ? 1 : 0,
     transition: 'opacity 0.2s',
-  },
-  searchWrapper: {
     display: 'flex',
     justifyContent: 'center',
     alignItems: 'center',
-  },
+  })
+);
+
+const ClearIcon = styled(Icons.clear)(({ theme }) => ({
+  color: theme.palette.primary.main,
+  cursor: 'pointer',
 }));
 
 const SearchInput: FC<SearchType> = props => {
   const { searchText = '', onClear = () => {}, onSearch = () => {} } = props;
   const [searchValue, setSearchValue] = useState<string>(searchText || '');
   const searched = useMemo(() => searchValue !== '', [searchValue]);
-  const classes = useSearchStyles({ searched });
   const { t: commonTrans } = useTranslation();
   const inputRef = useRef<any>(null);
 
@@ -90,67 +93,60 @@ const SearchInput: FC<SearchType> = props => {
 
   useEffect(() => {
     let hashPart = window.location.hash.substring(1);
-    // remove search part from hash part, include the '?'
     hashPart = hashPart.replace(/(\?search=)[^&]+(&?)/, '$2');
-
-    let searchPart = !searchValue
+    const searchPart = !searchValue
       ? ''
       : `?search=${encodeURIComponent(searchValue)}`;
-
-    hashPart = `${hashPart}${searchPart}`;
-
-    const newUrl = `${window.location.pathname}#${hashPart}`;
-
-    window.history.replaceState(null, '', newUrl);
-
+    window.history.replaceState(
+      null,
+      '',
+      `${window.location.pathname}#${hashPart}${searchPart}`
+    );
     handleSearch(searchValue);
   }, [searchValue]);
 
   return (
-    <div className={classes.wrapper}>
-      <TextField
+    <SearchWrapper>
+      <StyledTextField
+        searched={searched}
         inputRef={inputRef}
         variant="standard"
-        classes={{ root: classes.input }}
         InputProps={{
           disableUnderline: true,
-          classes: { input: classes.textfield },
+          startAdornment: (
+            <InputAdornment position="start">
+              <SearchIconWrapper
+                className="search-icon"
+                searched={searched}
+                onClick={() => handleSearch(searchValue)}
+              >
+                {Icons.search()}
+              </SearchIconWrapper>
+            </InputAdornment>
+          ),
           endAdornment: (
             <InputAdornment position="end">
-              <span
+              <IconWrapper
                 data-testid="clear-icon"
-                className={`flex-center ${classes.iconWrapper}`}
-                onClick={e => {
+                searched={searched}
+                onClick={() => {
                   setSearchValue('');
-                  inputRef.current.focus();
+                  inputRef.current?.focus();
                   onClear();
                 }}
               >
-                {Icons.clear({ classes: { root: classes.clearIcon } })}
-              </span>
-            </InputAdornment>
-          ),
-          startAdornment: (
-            <InputAdornment position="start">
-              <span
-                className={classes.searchWrapper}
-                onClick={() => handleSearch(searchValue)}
-              >
-                {Icons.search({ classes: { root: classes.searchIcon } })}
-              </span>
+                <ClearIcon />
+              </IconWrapper>
             </InputAdornment>
           ),
         }}
         onChange={e => {
           const value = e.target.value.trim();
           setSearchValue(value);
-          if (value === '') {
-            onClear();
-          }
+          if (value === '') onClear();
         }}
         onKeyPress={e => {
           if (e.key === 'Enter') {
-            // Do code here
             handleSearch(searchValue);
             e.preventDefault();
           }
@@ -158,7 +154,7 @@ const SearchInput: FC<SearchType> = props => {
         value={searchValue || ''}
         placeholder={commonTrans('search')}
       />
-    </div>
+    </SearchWrapper>
   );
 };
 

+ 32 - 41
client/src/components/customSelector/CustomGroupedSelect.tsx

@@ -3,41 +3,41 @@ import InputLabel from '@mui/material/InputLabel';
 import ListSubheader from '@mui/material/ListSubheader';
 import MenuItem from '@mui/material/MenuItem';
 import Select from '@mui/material/Select';
-import { makeStyles } from '@mui/styles';
+import { styled } from '@mui/material/styles';
 import { FC } from 'react';
-import type { Theme } from '@mui/material/styles';
 import type { GroupOption, ICustomGroupSelect } from './Types';
 
-const getStyles = makeStyles((theme: Theme) => ({
-  wrapper: {
-    width: '100%',
-  },
-  formControl: {
-    width: '100%',
-  },
-  groupName: {
-    paddingLeft: theme.spacing(2),
-    paddingRight: theme.spacing(2),
-    lineHeight: '32px',
-    color: theme.palette.text.primary,
-    fontWeight: 'bold',
-    fontSize: '12.8px',
-  },
-  menuItem: {
-    padding: theme.spacing(0, 4),
-    lineHeight: '24px',
+const Wrapper = styled('div')({
+  width: '100%',
+});
 
-    '&:hover': {
-      backgroundColor: 'rgba(18, 195, 244, 0.05)',
-    },
+const StyledFormControl = styled(FormControl)({
+  width: '100%',
+});
+
+const GroupName = styled(ListSubheader)(({ theme }) => ({
+  paddingLeft: theme.spacing(2),
+  paddingRight: theme.spacing(2),
+  lineHeight: '32px',
+  color: theme.palette.text.primary,
+  fontWeight: 'bold',
+  fontSize: '12.8px',
+}));
+
+const StyledMenuItem = styled(MenuItem)(({ theme }) => ({
+  padding: theme.spacing(0, 4),
+  lineHeight: '24px',
+
+  '&:hover': {
+    backgroundColor: 'rgba(18, 195, 244, 0.05)',
   },
-  menuItemSelected: {
+
+  '&.Mui-selected': {
     backgroundColor: 'rgba(0, 0, 0, 0.03)',
   },
 }));
 
 const CustomGroupedSelect: FC<ICustomGroupSelect> = props => {
-  const classes = getStyles();
   const {
     options,
     className = '',
@@ -51,26 +51,17 @@ const CustomGroupedSelect: FC<ICustomGroupSelect> = props => {
   const renderSelectGroup = (option: GroupOption) => {
     const items = option.children.map(child => {
       return (
-        <MenuItem
-          classes={{ root: classes.menuItem }}
-          key={child.value}
-          value={child.value}
-        >
+        <StyledMenuItem key={child.value} value={child.value}>
           {child.label}
-        </MenuItem>
+        </StyledMenuItem>
       );
     });
-    return [
-      <ListSubheader key={option.label} classes={{ root: classes.groupName }}>
-        {option.label}
-      </ListSubheader>,
-      items,
-    ];
+    return [<GroupName key={option.label}>{option.label}</GroupName>, items];
   };
 
   return (
-    <div className={`${classes.wrapper} ${className}`}>
-      <FormControl variant="filled" className={classes.formControl}>
+    <Wrapper className={className}>
+      <StyledFormControl variant="filled">
         {haveLabel && <InputLabel htmlFor="grouped-select">{label}</InputLabel>}
         <Select
           displayEmpty={!haveLabel}
@@ -91,8 +82,8 @@ const CustomGroupedSelect: FC<ICustomGroupSelect> = props => {
         >
           {options.map(option => renderSelectGroup(option))}
         </Select>
-      </FormControl>
-    </div>
+      </StyledFormControl>
+    </Wrapper>
   );
 };
 

+ 10 - 12
client/src/components/customSelector/CustomMultiSelector.tsx

@@ -4,17 +4,15 @@ import InputLabel from '@mui/material/InputLabel';
 import MenuItem from '@mui/material/MenuItem';
 import Select from '@mui/material/Select';
 import Checkbox from '@mui/material/Checkbox';
-import { withStyles } from '@mui/styles';
+import { styled } from '@mui/material/styles';
 import { generateId } from '../../utils/Common';
 import type { CustomMultiSelectorType } from './Types';
 
-const CustomMenuItem = withStyles({
-  root: {
-    minHeight: 'auto',
-    padding: '0 8px',
-    fontSize: '0.875rem',
-  },
-})(MenuItem);
+const StyledMenuItem = styled(MenuItem)({
+  minHeight: 'auto',
+  padding: '0 8px',
+  fontSize: '0.875rem',
+});
 
 const CustomSelector: FC<CustomMultiSelectorType> = props => {
   const {
@@ -36,12 +34,12 @@ const CustomSelector: FC<CustomMultiSelectorType> = props => {
   return (
     <FormControl variant={variant} className={wrapperClass} size={size}>
       {label && (
-        <InputLabel classes={{ root: labelClass }} htmlFor={id}>
+        <InputLabel className={labelClass} htmlFor={id}>
           {label}
         </InputLabel>
       )}
       <Select
-        classes={{ ...classes }}
+        className={classes?.root}
         {...others}
         multiple={true}
         value={values}
@@ -52,10 +50,10 @@ const CustomSelector: FC<CustomMultiSelectorType> = props => {
         renderValue={renderValue}
       >
         {options.map(v => (
-          <CustomMenuItem key={v.value} value={v.value}>
+          <StyledMenuItem key={v.value} value={v.value}>
             <Checkbox checked={values.indexOf(v.value as string) !== -1} />
             {v.label}
-          </CustomMenuItem>
+          </StyledMenuItem>
         ))}
       </Select>
     </FormControl>

+ 21 - 32
client/src/components/customSnackBar/CustomSnackBar.tsx

@@ -2,34 +2,24 @@ import { forwardRef, FC } from 'react';
 import MuiAlert from '@mui/material/Alert';
 import Snackbar from '@mui/material/Snackbar';
 import Slide from '@mui/material/Slide';
-import { makeStyles } from '@mui/styles';
-import type { Theme } from '@mui/material/styles';
+import { styled } from '@mui/material/styles';
 import type { AlertProps } from '@mui/material/Alert';
 import type { SlideProps } from '@mui/material/Slide';
 import type { CustomSnackBarType } from './Types';
 
-// if we need to use slide component
-// snackbar content must use forwardRef to wrapper it
-const Alert = forwardRef<HTMLDivElement, AlertProps>(
-  (props: { [x: string]: any }, ref) => {
-    return <MuiAlert ref={ref} elevation={6} variant="filled" {...props} />;
-  }
-);
+// Forward ref for Alert component
+const Alert = forwardRef<HTMLDivElement, AlertProps>((props, ref) => {
+  return <MuiAlert ref={ref} elevation={6} variant="filled" {...props} />;
+});
 
-interface SlideTransitionProps extends SlideProps {
-  direction?: 'left' | 'right' | 'up' | 'down';
-}
-
-const SlideTransition: React.FC<SlideTransitionProps> = props => {
+// SlideTransition component
+const SlideTransition: FC<SlideProps> = props => {
   return <Slide {...props} direction="left" />;
 };
 
-const useStyles = makeStyles((theme: Theme) => ({
-  root: {
-    maxWidth: '50vh',
-    wordBreak: 'break-all',
-  },
-  topRight: {
+// Styled components
+const StyledSnackbar = styled(Snackbar)(({ theme }) => ({
+  '&.MuiSnackbar-anchorOriginTopRight': {
     [theme.breakpoints.up('md')]: {
       top: '72px',
       right: theme.spacing(4),
@@ -39,6 +29,12 @@ const useStyles = makeStyles((theme: Theme) => ({
   },
 }));
 
+const StyledAlert = styled(Alert)({
+  maxWidth: '50vh',
+  wordBreak: 'break-all',
+});
+
+// CustomSnackBar component
 const CustomSnackBar: FC<CustomSnackBarType> = props => {
   const {
     vertical,
@@ -49,14 +45,14 @@ const CustomSnackBar: FC<CustomSnackBarType> = props => {
     message,
     onClose,
   } = props;
-  const classes = useStyles();
+
   const handleClose = (event: React.SyntheticEvent<any> | Event) => {
     onClose && onClose();
   };
 
   return (
     <div>
-      <Snackbar
+      <StyledSnackbar
         anchorOrigin={{
           vertical: vertical,
           horizontal: horizontal,
@@ -65,19 +61,12 @@ const CustomSnackBar: FC<CustomSnackBarType> = props => {
         open={open}
         onClose={handleClose}
         autoHideDuration={autoHideDuration}
-        classes={{
-          anchorOriginTopRight: classes.topRight,
-        }}
         TransitionComponent={SlideTransition}
       >
-        <Alert
-          onClose={handleClose}
-          severity={type}
-          classes={{ root: classes.root }}
-        >
+        <StyledAlert onClose={handleClose} severity={type}>
           {message}
-        </Alert>
-      </Snackbar>
+        </StyledAlert>
+      </StyledSnackbar>
     </div>
   );
 };

+ 0 - 36
client/src/components/customSwitch/CustomSwitch.tsx

@@ -1,36 +0,0 @@
-import FormControlLabel from '@mui/material/FormControlLabel';
-import Switch from '@mui/material/Switch';
-import { FC } from 'react';
-import { useTranslation } from 'react-i18next';
-import { makeStyles } from '@mui/styles';
-import type { Theme } from '@mui/material/styles';
-import type { CustomSwitchProps } from './Types';
-
-const getStyles = makeStyles((theme: Theme) => ({
-  label: {
-    color: '#757575',
-  },
-
-  placement: {
-    marginLeft: 0,
-  },
-}));
-
-const CustomSwitch: FC<CustomSwitchProps> = ({ onChange }) => {
-  const classes = getStyles();
-  const { t: commonTrans } = useTranslation();
-
-  return (
-    <FormControlLabel
-      classes={{
-        label: classes.label,
-        labelPlacementStart: classes.placement,
-      }}
-      label={commonTrans('view')}
-      labelPlacement="start"
-      control={<Switch color="primary" onChange={onChange} />}
-    />
-  );
-};
-
-export default CustomSwitch;

+ 0 - 3
client/src/components/customSwitch/Types.ts

@@ -1,3 +0,0 @@
-export interface CustomSwitchProps {
-  onChange: (event: React.ChangeEvent<{ checked: boolean }>) => void;
-}

+ 5 - 7
client/src/components/customToolTip/CustomToolTip.tsx

@@ -1,26 +1,24 @@
 import Tooltip from '@mui/material/Tooltip';
 import { FC } from 'react';
-import { makeStyles } from '@mui/styles';
+import { styled } from '@mui/material/styles';
 import type { Theme } from '@mui/material/styles';
 import type { CustomToolTipType } from './Types';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  tooltip: {},
+const StyledTooltip = styled(Tooltip)(({ theme }: { theme: Theme }) => ({
+  // You can add custom styles here if needed
 }));
 
 const CustomToolTip: FC<CustomToolTipType> = props => {
-  const classes = useStyles();
   const { title, placement = 'right', children, leaveDelay = 0 } = props;
   return (
-    <Tooltip
-      classes={{ tooltip: classes.tooltip }}
+    <StyledTooltip
       leaveDelay={leaveDelay}
       title={title}
       placement={placement}
       arrow
     >
       <span>{children}</span>
-    </Tooltip>
+    </StyledTooltip>
   );
 };
 

+ 27 - 58
client/src/components/grid/ActionBar.tsx

@@ -1,48 +1,40 @@
-import { FC } from 'react';
+import { FC, MouseEvent } from 'react';
 import IconButton from '@mui/material/IconButton';
 import Button from '@mui/material/Button';
-import Typography from '@mui/material/Typography';
-import { makeStyles } from '@mui/styles';
+import { styled } from '@mui/material/styles';
 import Icons from '../icons/Icons';
 import CustomToolTip from '../customToolTip/CustomToolTip';
-import type { Theme } from '@mui/material/styles';
 import type { ActionBarType } from './Types';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  root: {
+const Root = styled('span')<{ isHoverType: boolean }>(
+  ({ theme, isHoverType }) => ({
     position: 'relative',
     display: 'inline-block',
     marginRight: theme.spacing(1),
     color: theme.palette.text.primary,
     backgroundColor: theme.palette.background.paper,
-  },
-  tip: {
-    position: 'absolute',
-    left: 0,
-    bottom: '-10px',
-    fontSize: '10px',
-    textTransform: 'capitalize',
-    textAlign: 'center',
-    width: '100%',
-  },
-  disabled: {
+    ...(isHoverType && {
+      marginRight: 0,
+      '& button': {
+        color: theme.palette.text.primary,
+      },
+    }),
+  })
+);
+
+const DisabledIconButton = styled(IconButton)(({ theme }) => ({
+  '&.Mui-disabled': {
     opacity: 0.15,
   },
-  hoverType: {
-    marginRight: 0,
+}));
 
-    '& button': {
-      color: theme.palette.text.primary,
-    },
-  },
-  link: {
-    textDecoration: 'underline',
-    color: theme.palette.text.primary,
+const DisabledButton = styled(Button)(({ theme }) => ({
+  '&.Mui-disabled': {
+    opacity: 0.15,
   },
 }));
 
 const ActionBar: FC<ActionBarType> = props => {
-  const classes = useStyles();
   const { configs, row, isHoverType = false } = props;
 
   return (
@@ -51,60 +43,37 @@ const ActionBar: FC<ActionBarType> = props => {
         const label = v.getLabel ? v.getLabel(row) : v.label;
 
         return (
-          <span
-            className={`${classes.root} ${v.className} ${
-              isHoverType ? classes.hoverType : ''
-            }`}
-            key={i}
-          >
+          <Root className={v.className} isHoverType={isHoverType} key={i}>
             <CustomToolTip title={label || ''} placement="bottom">
               {v.icon ? (
-                <IconButton
+                <DisabledIconButton
                   aria-label={label || ''}
-                  onClickCapture={e => {
+                  onClickCapture={(e: MouseEvent<HTMLButtonElement>) => {
                     e.stopPropagation();
                     v.onClick(e, row);
                   }}
                   disabled={v.disabled ? v.disabled(row) : false}
-                  classes={{
-                    disabled: classes.disabled,
-                  }}
                   size="large"
                 >
                   {v.showIconMethod === 'renderFn'
                     ? v.renderIconFn && v.renderIconFn(row)
                     : Icons[v.icon]()}
-                </IconButton>
-              ) : v.linkButton ? (
-                <Typography
-                  component="a"
-                  href="#/users"
-                  className={classes.link}
-                  onClick={e => {
-                    e.stopPropagation();
-                    v.onClick(e, row);
-                  }}
-                >
-                  {v.text}
-                </Typography>
+                </DisabledIconButton>
               ) : (
-                <Button
+                <DisabledButton
                   aria-label={label || ''}
-                  onClickCapture={e => {
+                  onClickCapture={(e: MouseEvent<HTMLButtonElement>) => {
                     e.stopPropagation();
                     v.onClick(e, row);
                   }}
                   size="small"
                   disabled={v.disabled ? v.disabled(row) : false}
-                  classes={{
-                    disabled: classes.disabled,
-                  }}
                 >
                   {v.text}
-                </Button>
+                </DisabledButton>
               )}
             </CustomToolTip>
-          </span>
+          </Root>
         );
       })}
     </>

+ 8 - 27
client/src/components/grid/Grid.tsx

@@ -1,4 +1,4 @@
-import { FC, MouseEvent, useRef, useEffect, useState } from 'react';
+import { FC, MouseEvent, useRef, useEffect } from 'react';
 import { useTranslation } from 'react-i18next';
 import Typography from '@mui/material/Typography';
 import Grid from '@mui/material/Grid';
@@ -12,6 +12,13 @@ import type { Theme } from '@mui/material/styles/createTheme';
 import type { AttuGridType } from './Types';
 
 const userStyle = makeStyles((theme: Theme) => ({
+  wrapper: {
+    height: '100%',
+  },
+  container: {
+    flexWrap: 'nowrap',
+    flexDirection: 'column',
+  },
   loading: {
     height: '100%',
     display: 'flex',
@@ -20,22 +27,11 @@ const userStyle = makeStyles((theme: Theme) => ({
     padding: theme.spacing(20),
     width: '100%',
   },
-
   tableTitle: {
     '& .last': {
       color: 'rgba(0, 0, 0, 0.54)',
     },
   },
-  noData: {
-    pointerEvents: 'none',
-    color:  theme.palette.text.secondary,
-    textAlign: 'center',
-    height: '50vh',
-    display: 'grid',
-    justifyContent: 'center',
-    alignContent: 'center',
-    fontSize: '32px',
-  },
   pagenation: {
     '& .MuiTablePagination-spacer': {
       display: 'none',
@@ -58,21 +54,11 @@ const userStyle = makeStyles((theme: Theme) => ({
       },
     },
   },
-
   noBottomPadding: {
     paddingBottom: '0 !important',
     display: 'flex',
     flexDirection: 'column',
   },
-
-  wrapper: {
-    height: '100%',
-    
-  },
-  container: {
-    flexWrap: 'nowrap',
-    flexDirection: 'column',
-  },
 }));
 
 /**
@@ -103,7 +89,6 @@ const userStyle = makeStyles((theme: Theme) => ({
 const AttuGrid: FC<AttuGridType> = props => {
   const classes = userStyle();
   const tableRef = useRef<HTMLDivElement | null>(null);
-  const [loadingRowCount, setLoadingRowCount] = useState<number>(0);
 
   // i18n
   const { t: commonTrans } = useTranslation();
@@ -200,9 +185,6 @@ const AttuGrid: FC<AttuGridType> = props => {
 
       const rowCount = Math.floor(totalHeight / rowHeight);
 
-      // fix loading mask
-      setLoadingRowCount(rowCount);
-
       if (setRowsPerPage) {
         setRowsPerPage(rowCount);
       }
@@ -261,7 +243,6 @@ const AttuGrid: FC<AttuGridType> = props => {
 
       <Grid item xs={12} className={classes.noBottomPadding}>
         <Table
-          loadingRowCount={loadingRowCount}
           openCheckBox={openCheckBox}
           primaryKey={primaryKey}
           rows={rows}

+ 2 - 2
client/src/components/grid/LoadingTable.tsx

@@ -3,8 +3,8 @@ import Typography from '@mui/material/Typography';
 import Box from '@mui/material/Box';
 import { useTranslation } from 'react-i18next';
 
-const LoadingTable = (props: { wrapperClass?: string; count: number }) => {
-  const { wrapperClass = '', count } = props;
+const LoadingTable = (props: { wrapperClass?: string }) => {
+  const { wrapperClass = '' } = props;
   const { t: btnTrans } = useTranslation('btn');
 
   return (

+ 1 - 2
client/src/components/grid/Table.tsx

@@ -119,7 +119,6 @@ const EnhancedTable: FC<TableType> = props => {
     handleSort,
     order,
     orderBy,
-    loadingRowCount,
   } = props;
   const classes = useStyles({ tableCellMaxWidth });
   const { t: commonTrans } = useTranslation();
@@ -286,7 +285,7 @@ const EnhancedTable: FC<TableType> = props => {
           </Table>
         )}
 
-        {isLoading && <LoadingTable count={loadingRowCount} />}
+        {isLoading && <LoadingTable />}
       </Box>
     </TableContainer>
   );

+ 10 - 20
client/src/components/grid/TableEditableHead.tsx

@@ -2,37 +2,27 @@ import { FC } from 'react';
 import TableHead from '@mui/material/TableHead';
 import TableRow from '@mui/material/TableRow';
 import TableCell from '@mui/material/TableCell';
-import { makeStyles } from '@mui/styles';
-import type { Theme } from '@mui/material/styles';
+import { styled } from '@mui/material/styles';
 import type { TableEditableHeadType } from './Types';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  tableCell: {
-    paddingLeft: theme.spacing(2),
-  },
-  tableHeader: {
-    textTransform: 'capitalize',
-    color: 'rgba(0, 0, 0, 0.6)',
-    fontSize: '12.8px',
-  },
-  tableRow: {
-    // borderBottom: '1px solid rgba(0, 0, 0, 0.6);',
-  },
+const StyledTableCell = styled(TableCell)(({ theme }) => ({
+  paddingLeft: theme.spacing(2),
+}));
+
+const StyledTableRow = styled(TableRow)(({ theme }) => ({
+  // borderBottom: '1px solid rgba(0, 0, 0, 0.6);',
 }));
 
 const EditableTableHead: FC<TableEditableHeadType> = props => {
   const { editHeads } = props;
-  const classes = useStyles();
 
   return (
     <TableHead>
-      <TableRow className={classes.tableRow}>
+      <StyledTableRow>
         {editHeads.map((headCell, index) => (
-          <TableCell key={index} className={classes.tableCell}>
-            {headCell.component}
-          </TableCell>
+          <StyledTableCell key={index}>{headCell.component}</StyledTableCell>
         ))}
-      </TableRow>
+      </StyledTableRow>
     </TableHead>
   );
 };

+ 38 - 41
client/src/components/grid/TableHead.tsx

@@ -6,37 +6,35 @@ import TableCell from '@mui/material/TableCell';
 import Checkbox from '@mui/material/Checkbox';
 import TableSortLabel from '@mui/material/TableSortLabel';
 import Typography from '@mui/material/Typography';
-import { makeStyles } from '@mui/styles';
-import type { Theme } from '@mui/material/styles';
+import { styled } from '@mui/material/styles';
 import type { TableHeadType } from './Types';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  visuallyHidden: {
-    border: 0,
-    clip: 'rect(0 0 0 0)',
-    height: 1,
-    margin: -1,
-    overflow: 'hidden',
-    padding: 0,
-    position: 'absolute',
-    top: 20,
-    width: 1,
-  },
-  tableCell: {
-    // background: theme.palette.common.t,
-    padding: 0,
-    // borderBottom: 'none',
-  },
-  tableHeader: {
-    padding: theme.spacing(1.5),
-    fontWeight: 500,
-    maxHeight: 45,
-    overflow: 'hidden',
-    whiteSpace: 'nowrap',
-  },
-  tableRow: {
-    // borderBottom: '1px solid rgba(0, 0, 0, 0.6);',
-  },
+const VisuallyHiddenTypography = styled(Typography)(({ theme }) => ({
+  border: 0,
+  clip: 'rect(0 0 0 0)',
+  height: 1,
+  margin: -1,
+  overflow: 'hidden',
+  padding: 0,
+  position: 'absolute',
+  top: 20,
+  width: 1,
+}));
+
+const StyledTableCell = styled(TableCell)(({ theme }) => ({
+  padding: 0,
+}));
+
+const StyledTableHeader = styled(Typography)(({ theme }) => ({
+  padding: theme.spacing(1.5),
+  fontWeight: 500,
+  maxHeight: 45,
+  overflow: 'hidden',
+  whiteSpace: 'nowrap',
+}));
+
+const StyledTableRow = styled(TableRow)(({ theme }) => ({
+  // borderBottom: '1px solid rgba(0, 0, 0, 0.6);',
 }));
 
 const EnhancedTableHead: FC<TableHeadType> = props => {
@@ -51,7 +49,7 @@ const EnhancedTableHead: FC<TableHeadType> = props => {
     openCheckBox,
     disableSelect,
   } = props;
-  const classes = useStyles();
+
   const createSortHandler = (property: string) => (event: React.MouseEvent) => {
     handleSort &&
       handleSort(
@@ -63,7 +61,7 @@ const EnhancedTableHead: FC<TableHeadType> = props => {
 
   return (
     <TableHead>
-      <TableRow className={classes.tableRow}>
+      <StyledTableRow>
         {openCheckBox && (
           <TableCell padding="checkbox" role="cell">
             <Checkbox
@@ -91,7 +89,7 @@ const EnhancedTableHead: FC<TableHeadType> = props => {
             headCell.headerFormatter || (v => <>{v.label}</>);
 
           return (
-            <TableCell
+            <StyledTableCell
               key={headCell.id + headCell.label}
               align={headCell.align || 'left'}
               padding={headCell.disablePadding ? 'none' : 'normal'}
@@ -99,7 +97,6 @@ const EnhancedTableHead: FC<TableHeadType> = props => {
                 orderBy === (headCell.sortBy || headCell.id) ? order : false
               }
               style={cellStyle}
-              className={classes.tableCell}
               role="cell"
             >
               {headCell.label && handleSort && !headCell.notSort ? (
@@ -110,27 +107,27 @@ const EnhancedTableHead: FC<TableHeadType> = props => {
                   }
                   onClick={createSortHandler(headCell.sortBy || headCell.id)}
                 >
-                  <Typography variant="body1" className={classes.tableHeader}>
+                  <StyledTableHeader variant="body1">
                     {headerFormatter(headCell)}
-                  </Typography>
+                  </StyledTableHeader>
 
                   {orderBy === (headCell.sortBy || headCell.id) ? (
-                    <Typography className={classes.visuallyHidden}>
+                    <VisuallyHiddenTypography>
                       {order === 'desc'
                         ? 'sorted descending'
                         : 'sorted ascending'}
-                    </Typography>
+                    </VisuallyHiddenTypography>
                   ) : null}
                 </TableSortLabel>
               ) : (
-                <Typography variant="body1" className={classes.tableHeader}>
+                <StyledTableHeader variant="body1">
                   {headerFormatter(headCell)}
-                </Typography>
+                </StyledTableHeader>
               )}
-            </TableCell>
+            </StyledTableCell>
           );
         })}
-      </TableRow>
+      </StyledTableRow>
     </TableHead>
   );
 };

+ 26 - 32
client/src/components/grid/TablePaginationActions.tsx

@@ -3,32 +3,30 @@ import CustomButton from '../customButton/CustomButton';
 import icons from '../icons/Icons';
 import React from 'react';
 import { useTranslation } from 'react-i18next';
-import { makeStyles } from '@mui/styles';
-import type { Theme } from '@mui/material/styles';
+import { styled } from '@mui/material/styles';
 import type { TablePaginationActionsProps } from './Types';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  root: {
-    display: 'flex',
-    alignItems: 'center',
-    flexShrink: 0,
-  },
-  page: {
-    display: 'flex',
-    justifyContent: 'center',
-    alignItems: 'center',
-    width: '24px',
-    height: '24px',
-  },
-  btn: {
-    paddingLeft: 8,
-    paddingRight: 8,
-    minWidth: '24px',
-  },
+const Root = styled('div')(({ theme }) => ({
+  display: 'flex',
+  alignItems: 'center',
+  flexShrink: 0,
+}));
+
+const PageNumber = styled(Typography)(({ theme }) => ({
+  display: 'flex',
+  justifyContent: 'center',
+  alignItems: 'center',
+  width: '24px',
+  height: '24px',
+}));
+
+const StyledButton = styled(CustomButton)(({ theme }) => ({
+  paddingLeft: 8,
+  paddingRight: 8,
+  minWidth: '24px',
 }));
 
 const TablePaginationActions = (props: TablePaginationActionsProps) => {
-  const classes = useStyles();
   const { count, page, rowsPerPage, onPageChange } = props;
 
   // icons
@@ -52,27 +50,23 @@ const TablePaginationActions = (props: TablePaginationActionsProps) => {
   };
 
   return (
-    <div className={classes.root}>
-      <CustomButton
+    <Root>
+      <StyledButton
         onClick={handleBackButtonClick}
         disabled={page === 0}
         aria-label={gridTrans.prevLabel}
-        className={classes.btn}
       >
         <PrevIcon />
-      </CustomButton>
-      <Typography variant="body2" className={classes.page}>
-        {page + 1}
-      </Typography>
-      <CustomButton
+      </StyledButton>
+      <PageNumber variant="body2">{page + 1}</PageNumber>
+      <StyledButton
         onClick={handleNextButtonClick}
         disabled={page >= Math.ceil(count / rowsPerPage) - 1}
         aria-label={gridTrans.nextLabel}
-        className={classes.btn}
       >
         <NextIcon />
-      </CustomButton>
-    </div>
+      </StyledButton>
+    </Root>
   );
 };
 

+ 0 - 58
client/src/components/grid/TableSwitch.tsx

@@ -1,58 +0,0 @@
-import { FC, useState } from 'react';
-import Icons from '../icons/Icons';
-import { makeStyles } from '@mui/styles';
-import type { Theme } from '@mui/material/styles';
-import type { TableSwitchType } from './Types';
-
-const useStyles = makeStyles((theme: Theme) => ({
-  root: {
-    display: 'flex',
-  },
-  line: {
-    display: 'inline-block',
-    margin: theme.spacing(0, 1),
-    border: '1px solid rgba(0, 0, 0, 0.15)',
-  },
-  btn: {
-    cursor: 'pointer',
-    color: 'rgba(0, 0, 0, 0.15)',
-  },
-  active: {
-    color: 'rgba(0, 0, 0, 0.6) ',
-  },
-}));
-
-const TableSwitch: FC<TableSwitchType> = props => {
-  const { defaultActive = 'list', onListClick, onAppClick } = props;
-  const [active, setActive] = useState(defaultActive);
-  const classes = useStyles();
-  const IconList = Icons.list;
-  const IconApp = Icons.app;
-
-  const handleListClick = () => {
-    setActive('list');
-    onListClick();
-  };
-
-  const handleAppClick = () => {
-    setActive('app');
-    onAppClick();
-  };
-
-  return (
-    <div className={classes.root}>
-      <IconList
-        className={`${classes.btn} ${active === 'list' ? classes.active : ''}`}
-        role="button"
-        onClick={handleListClick}
-      />
-      <span className={classes.line}></span>
-      <IconApp
-        className={`${classes.btn} ${active === 'app' ? classes.active : ''}`}
-        onClick={handleAppClick}
-      />
-    </div>
-  );
-};
-
-export default TableSwitch;

+ 0 - 14
client/src/components/grid/ToolBar.tsx

@@ -5,7 +5,6 @@ import IconButton from '@mui/material/IconButton';
 import CustomButton from '../customButton/CustomButton';
 import Icons from '../icons/Icons';
 import SearchInput from '../customInput/SearchInput';
-import TableSwitch from './TableSwitch';
 import { throwErrorForDev } from '../../utils/Common';
 import CustomIconButton from '../customButton/CustomIconButton';
 import { makeStyles } from '@mui/styles';
@@ -136,19 +135,6 @@ const CustomToolBar: FC<ToolBarType> = props => {
                 );
               }
               switch (c.type) {
-                case 'switch':
-                  if (!c.onAppClick || !c.onListClick) {
-                    return throwErrorForDev(
-                      `if type is switch need onAppClick onListClick event handler`
-                    );
-                  }
-                  return (
-                    <TableSwitch
-                      onAppClick={c.onAppClick}
-                      onListClick={c.onListClick}
-                      key={i}
-                    />
-                  );
                 case 'select':
                 case 'groupSelect':
                   if (!c.component) {

+ 0 - 2
client/src/components/grid/Types.ts

@@ -95,7 +95,6 @@ export type TableType = {
   handleSort?: (e: any, orderBy: string) => void;
   order?: SortDirection;
   orderBy?: string;
-  loadingRowCount: number;
   ref?: Ref<HTMLDivElement>;
 };
 
@@ -167,7 +166,6 @@ type ActionBarConfig = {
   onClick: (e: React.MouseEvent, row: any) => void;
   icon?: IconsType;
   text?: string;
-  linkButton?: boolean;
   showIconMethod?: 'iconType' | 'renderFn';
   renderIconFn?: (row: any) => ReactElement;
   label?: string;

+ 93 - 103
client/src/components/layout/Header.tsx

@@ -11,88 +11,90 @@ import CustomSelector from '@/components/customSelector/CustomSelector';
 import StatusIcon from '@/components/status/StatusIcon';
 import UpdateUser from '@/pages/user/dialogs/UpdateUserPassDialog';
 import icons from '../icons/Icons';
-import { makeStyles } from '@mui/styles';
+import { styled } from '@mui/material/styles';
 import IconButton from '@mui/material/IconButton';
 import { ColorModeContext } from '@/context';
 import { LoadingType } from '@/components/status/StatusIcon';
-import type { Theme } from '@mui/material/styles';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  header: {
-    display: 'flex',
-    alignItems: 'center',
-    color: theme.palette.text.primary,
-    backgroundColor: theme.palette.background.paper,
-    paddingRight: theme.spacing(1),
-    borderBottom: `1px solid ${theme.palette.divider}`,
-    height: 48,
-  },
-  contentWrapper: {
-    display: 'flex',
-    justifyContent: 'space-between',
-    alignItems: 'center',
-    flex: 1,
-    height: 48,
-  },
-  navigation: {
-    display: 'flex',
-    alignItems: 'center',
-  },
-  icon: {
-    color: theme.palette.primary.main,
-    cursor: 'pointer',
-    marginRight: theme.spacing(1),
-    width: '20px',
-  },
-  addressWrapper: {
-    display: 'flex',
-    alignItems: 'center',
+const HeaderWrapper = styled('header')(({ theme }) => ({
+  display: 'flex',
+  alignItems: 'center',
+  color: theme.palette.text.primary,
+  backgroundColor: theme.palette.background.paper,
+  paddingRight: theme.spacing(1),
+  borderBottom: `1px solid ${theme.palette.divider}`,
+  height: 48,
+}));
 
-    '& .text': {
-      marginRight: theme.spacing(2),
+const ContentWrapper = styled('div')({
+  display: 'flex',
+  justifyContent: 'space-between',
+  alignItems: 'center',
+  flex: 1,
+  height: 48,
+});
 
-      '& .address': {
-        fontSize: '12px',
-        lineHeight: 1.3,
-      },
+const Navigation = styled('div')({
+  display: 'flex',
+  alignItems: 'center',
+});
 
-      '& .status': {
-        fontSize: '12px',
-        lineHeight: 1.3,
-        color: '#1ba954',
-      },
+const StyledIcon = styled('div')(({ theme }) => ({
+  color: theme.palette.primary.main,
+  cursor: 'pointer',
+  marginRight: theme.spacing(1),
+}));
+
+const AddressWrapper = styled('div')(({ theme }) => ({
+  display: 'flex',
+  alignItems: 'center',
+
+  '& .text': {
+    marginRight: theme.spacing(2),
+
+    '& .address': {
+      fontSize: '12px',
+      lineHeight: 1.3,
     },
-  },
-  title: {
-    paddingLeft: theme.spacing(2),
-  },
-  database: {
-    transform: 'translateY(-4px)',
-    width: theme.spacing(16),
-    '& .MuiInputLabel-root': {
-      top: '4px',
+
+    '& .status': {
+      fontSize: '12px',
+      lineHeight: 1.3,
+      color: '#1ba954',
     },
   },
-  modeBtn: {
-    marginRight: theme.spacing(1),
-    '& svg': {
-      fontSize: 18,
-    },
+}));
+
+const Title = styled(Typography)(({ theme }) => ({
+  paddingLeft: theme.spacing(2),
+}));
+
+const DatabaseSelector = styled(CustomSelector)(({ theme }) => ({
+  transform: 'translateY(-4px)',
+  width: theme.spacing(16),
+  '& .MuiInputLabel-root': {
+    top: '4px',
+  },
+}));
+
+const ModeButton = styled(IconButton)(({ theme }) => ({
+  marginRight: theme.spacing(1),
+  '& svg': {
+    fontSize: 18,
     color: theme.palette.text.primary,
   },
-  extra: {
-    marginLeft: theme.spacing(0.5),
-    display: 'flex',
-    '& svg': {
-      fontSize: 15,
-      color: theme.palette.primary.main,
-    },
+}));
+
+const Extra = styled('span')(({ theme }) => ({
+  marginLeft: theme.spacing(0.5),
+  display: 'flex',
+  '& svg': {
+    fontSize: 15,
+    color: theme.palette.primary.main,
   },
 }));
 
 const Header: FC = () => {
-  // styles
-  const classes = useStyles();
   // use context
   const { navInfo } = useContext(navContext);
   const { mode, toggleColorMode } = useContext(ColorModeContext);
@@ -112,6 +114,7 @@ const Header: FC = () => {
   const statusTrans = commonTrans('status');
   const { t: dbTrans } = useTranslation('database');
   const { t: successTrans } = useTranslation('success');
+  const { t: userTrans } = useTranslation('user');
 
   // icons
   const BackIcon = icons.back;
@@ -170,18 +173,17 @@ const Header: FC = () => {
   const isLoadingDb = dbOptions.length === 0;
 
   return (
-    <header className={classes.header}>
-      <div className={classes.contentWrapper}>
-        <div className={classes.navigation}>
+    <HeaderWrapper>
+      <ContentWrapper>
+        <Navigation>
           {navInfo.backPath !== '' && (
-            <BackIcon
-              classes={{ root: classes.icon }}
-              onClick={() => handleBack(navInfo.backPath)}
-            />
+            <StyledIcon onClick={() => handleBack(navInfo.backPath)}>
+              <BackIcon />
+            </StyledIcon>
           )}
           {navInfo.showDatabaseSelector &&
             (!isLoadingDb ? (
-              <CustomSelector
+              <DatabaseSelector
                 label={dbTrans('database')}
                 value={database}
                 onChange={async (e: { target: { value: unknown } }) => {
@@ -196,31 +198,22 @@ const Header: FC = () => {
                 }}
                 options={dbOptions}
                 variant="filled"
-                wrapperClass={classes.database}
                 disabled={loading}
               />
             ) : (
               <StatusIcon type={LoadingType.CREATING} />
             ))}
 
-          <Typography
-            variant="h5"
-            color="textPrimary"
-            className={classes.title}
-          >
+          <Title variant="h5" color="textPrimary">
             {navInfo.navTitle}
-          </Typography>
-          <span className={classes.extra}>{navInfo.extra}</span>
-        </div>
+          </Title>
+          <Extra>{navInfo.extra}</Extra>
+        </Navigation>
 
-        <div className={classes.addressWrapper}>
-          <IconButton
-            className={classes.modeBtn}
-            onClick={toggleColorMode}
-            color="inherit"
-          >
+        <AddressWrapper>
+          <ModeButton onClick={toggleColorMode} color="inherit">
             {mode === 'dark' ? <icons.night /> : <icons.day />}
-          </IconButton>
+          </ModeButton>
           <div className="text">
             <Typography className="address">{address}</Typography>
             <Typography className="status">{statusTrans.running}</Typography>
@@ -228,12 +221,12 @@ const Header: FC = () => {
           {username && (
             <>
               <Tooltip title={username}>
-                <div
+                <StyledIcon
                   onClick={handleUserMenuClick}
                   style={{ cursor: 'pointer' }}
                 >
-                  <Avatar classes={{ root: classes.icon }} />
-                </div>
+                  <Avatar />
+                </StyledIcon>
               </Tooltip>
               <Menu
                 anchorEl={anchorEl}
@@ -249,22 +242,19 @@ const Header: FC = () => {
                 }}
               >
                 <MenuItem onClick={handleChangePassword}>
-                  Change Password
+                  {userTrans('changePassword')}
                 </MenuItem>
               </Menu>
             </>
           )}
           <Tooltip title={'disconnect'}>
-            <div>
-              <LogoutIcon
-                classes={{ root: classes.icon }}
-                onClick={handleLogout}
-              />
-            </div>
+            <StyledIcon>
+              <LogoutIcon onClick={handleLogout} />
+            </StyledIcon>
           </Tooltip>
-        </div>
-      </div>
-    </header>
+        </AddressWrapper>
+      </ContentWrapper>
+    </HeaderWrapper>
   );
 };
 

+ 30 - 35
client/src/components/layout/Wrapper.tsx

@@ -1,5 +1,5 @@
-import { makeStyles } from '@mui/styles';
 import { useTranslation } from 'react-i18next';
+import { styled } from '@mui/material/styles';
 import type { Theme } from '@mui/material/styles';
 
 interface WrapperProps {
@@ -8,30 +8,30 @@ interface WrapperProps {
   className?: string;
 }
 
-const useStyles = makeStyles((theme: Theme) => ({
-  wrapper: {
-    width: '100%',
-    height: '100%',
-    position: 'relative',
-  },
-  overlay: {
-    position: 'absolute',
-    top: 0,
-    left: 0,
-    right: 0,
-    bottom: 0,
-    display: 'flex',
-    alignItems: 'center',
-    justifyContent: 'center',
-    backgroundColor: theme.palette.background.default,
-    zIndex: 1000,
-  },
-  message: {
-    fontSize: 14,
-    color: theme.palette.text.primary,
-    textAlign: 'center',
-    fontStyle: 'italic',
-  },
+const WrapperRoot = styled('div')({
+  width: '100%',
+  height: '100%',
+  position: 'relative',
+});
+
+const Overlay = styled('div')(({ theme }: { theme: Theme }) => ({
+  position: 'absolute',
+  top: 0,
+  left: 0,
+  right: 0,
+  bottom: 0,
+  display: 'flex',
+  alignItems: 'center',
+  justifyContent: 'center',
+  backgroundColor: theme.palette.background.default,
+  zIndex: 1000,
+}));
+
+const Message = styled('div')(({ theme }: { theme: Theme }) => ({
+  fontSize: 14,
+  color: theme.palette.text.primary,
+  textAlign: 'center',
+  fontStyle: 'italic',
 }));
 
 const Wrapper = ({
@@ -39,23 +39,18 @@ const Wrapper = ({
   children,
   className,
 }: WrapperProps) => {
-  // styles
-  const classes = useStyles();
-
   // i18n
   const { t: commonTrans } = useTranslation();
 
   return (
-    <div className={`${classes.wrapper} ${className}`}>
+    <WrapperRoot className={className}>
       {children}
       {!hasPermission && (
-        <div className={classes.overlay}>
-          <div className={classes.message}>
-            {commonTrans('noPermissionTip')}
-          </div>
-        </div>
+        <Overlay>
+          <Message>{commonTrans('noPermissionTip')}</Message>
+        </Overlay>
       )}
-    </div>
+    </WrapperRoot>
   );
 };
 

+ 41 - 48
client/src/components/status/Status.tsx

@@ -3,59 +3,55 @@ import StatusIcon from '@/components/status/StatusIcon';
 import { useTranslation } from 'react-i18next';
 import Typography from '@mui/material/Typography';
 import { useTheme } from '@mui/material/styles';
-import { makeStyles } from '@mui/styles';
+import { styled, keyframes } from '@mui/system';
 import { LOADING_STATE } from '@/consts';
 import { LoadingType } from '@/components/status/StatusIcon';
-import type { Theme } from '@mui/material/styles';
 
 export type StatusType = {
   status: LOADING_STATE;
   percentage?: number;
 };
 
-const useStyles = makeStyles((theme: Theme) => ({
-  root: {
-    display: 'flex',
-    alignItems: 'center',
-  },
-  label: {
-    color: theme.palette.text.secondary,
-    textTransform: 'capitalize',
-  },
-  circle: {
-    backgroundColor: (props: any) => props.color,
-    borderRadius: '50%',
-    width: '10px',
-    height: '10px',
-    marginRight: theme.spacing(0.5),
-  },
+const Root = styled('div')({
+  display: 'flex',
+  alignItems: 'center',
+});
 
-  loading: {
-    marginRight: '10px',
-  },
-
-  flash: {
-    animation: '$bgColorChange 1.5s infinite',
-  },
+const Label = styled(Typography)(({ theme }) => ({
+  color: theme.palette.text.secondary,
+  textTransform: 'capitalize',
+}));
 
-  '@keyframes bgColorChange': {
-    '0%': {
-      backgroundColor: (props: any) => props.color,
-    },
-    '50%': {
-      backgroundColor: 'transparent',
-    },
-    '100%': {
-      backgroundColor: (props: any) => props.color,
-    },
-  },
+const Circle = styled('div')(({ color }) => ({
+  backgroundColor: color,
+  borderRadius: '50%',
+  width: '10px',
+  height: '10px',
+  marginRight: '4px', // Equivalent to theme.spacing(0.5)
 }));
 
+const LoadingIconWrapper = styled('div')({
+  marginRight: '10px',
+});
+
+const bgColorChange = keyframes`
+  0% {
+    background-color: inherit;
+  }
+  50% {
+    background-color: transparent;
+  }
+  100% {
+    background-color: inherit;
+  }
+`;
+
 const Status: FC<StatusType> = props => {
   const { status, percentage = 0 } = props;
   const { t: commonTrans } = useTranslation();
   const theme = useTheme();
   const statusTrans = commonTrans('status');
+
   const { label, color } = useMemo(() => {
     switch (status) {
       case LOADING_STATE.UNLOADED:
@@ -69,6 +65,7 @@ const Status: FC<StatusType> = props => {
           label: statusTrans.loaded,
           color: '#06f3af',
         };
+
       case LOADING_STATE.LOADING:
         return {
           label: `${percentage}% ${statusTrans.loading}`,
@@ -81,23 +78,19 @@ const Status: FC<StatusType> = props => {
           color: '#f25c06',
         };
     }
-  }, [status, statusTrans, percentage]);
-
-  const classes = useStyles({ color });
+  }, [status, statusTrans, percentage, theme.palette.primary.main]);
 
   return (
-    <div className={classes.root}>
-      {status === LOADING_STATE.LOADED && (
-        <div className={classes.circle}></div>
-      )}
+    <Root>
+      {status === LOADING_STATE.LOADED && <Circle color={color} />}
       {status === LOADING_STATE.LOADING && (
-        <StatusIcon type={LoadingType.CREATING} className={classes.loading} />
+        <LoadingIconWrapper>
+          <StatusIcon type={LoadingType.CREATING} />
+        </LoadingIconWrapper>
       )}
 
-      <Typography variant="body2" className={classes.label}>
-        {label}
-      </Typography>
-    </div>
+      <Label variant="body2">{label}</Label>
+    </Root>
   );
 };
 

+ 12 - 23
client/src/components/status/StatusIcon.tsx

@@ -1,7 +1,6 @@
 import CircularProgress from '@mui/material/CircularProgress';
 import { FC, ReactElement } from 'react';
-import { makeStyles } from '@mui/styles';
-import type { Theme } from '@mui/material/styles';
+import { styled } from '@mui/system';
 
 export enum LoadingType {
   CREATING = 'creating',
@@ -15,32 +14,24 @@ export type StatusIconType = {
   size?: number;
 };
 
-const useStyles = makeStyles((theme: Theme) => ({
-  wrapper: {
-    display: 'flex',
-    justifyContent: 'flex-left',
-    alignItems: 'center',
-    paddingLeft: theme.spacing(0.5),
-  },
-  svg: {
-    color: theme.palette.primary.main,
-  },
+const Wrapper = styled('div')(({ theme }) => ({
+  display: 'flex',
+  justifyContent: 'flex-start',
+  alignItems: 'center',
+  paddingLeft: theme.spacing(0.5),
+}));
+
+const StyledCircularProgress = styled(CircularProgress)(({ theme }) => ({
+  color: theme.palette.primary.main,
 }));
 
 const StatusIcon: FC<StatusIconType> = props => {
-  const classes = useStyles();
   const { type, className = '', size = 16 } = props;
 
   const getElement = (type: LoadingType): ReactElement => {
     switch (type) {
       case 'creating':
-        return (
-          <CircularProgress
-            size={size}
-            thickness={4}
-            classes={{ svg: classes.svg }}
-          />
-        );
+        return <StyledCircularProgress size={size} thickness={4} />;
       case 'finish':
         return <></>;
       default:
@@ -48,9 +39,7 @@ const StatusIcon: FC<StatusIconType> = props => {
     }
   };
 
-  return (
-    <div className={`${classes.wrapper} ${className}`}>{getElement(type)}</div>
-  );
+  return <Wrapper className={className}>{getElement(type)}</Wrapper>;
 };
 
 export default StatusIcon;

+ 1 - 8
client/src/components/uploader/Uploader.tsx

@@ -2,14 +2,8 @@ import { FC, useContext, useRef } from 'react';
 import { rootContext } from '@/context';
 import CustomButton from '../customButton/CustomButton';
 import { FILE_MIME_TYPE } from '@/consts';
-import { makeStyles } from '@mui/styles';
-import type { Theme } from '@mui/material/styles';
 import type { UploaderProps } from './Types';
 
-const getStyles = makeStyles((theme: Theme) => ({
-  btn: {},
-}));
-
 const Uploader: FC<UploaderProps> = ({
   label,
   accept,
@@ -25,7 +19,6 @@ const Uploader: FC<UploaderProps> = ({
 }) => {
   const inputRef = useRef(null);
   const type = useRef<FILE_MIME_TYPE>(FILE_MIME_TYPE.CSV);
-  const classes = getStyles();
 
   const { openSnackBar } = useContext(rootContext);
 
@@ -75,7 +68,7 @@ const Uploader: FC<UploaderProps> = ({
     <section>
       <CustomButton
         variant="contained"
-        className={`${classes.btn} ${btnClass}`}
+        className={`${btnClass}`}
         onClick={handleUpload}
         disabled={disabled}
         tooltip={disabled ? disableTooltip : ''}

+ 1 - 0
client/src/i18n/cn/user.ts

@@ -11,6 +11,7 @@ const userTrans = {
   newPassword: '新密码',
   confirmPassword: '确认密码',
   update: '更新密码',
+  changePassword: '修改密码',
   isNotSame: '与新密码不同',
   deleteTip: '请至少选择一个要删除的用户,不能删除root用户。',
   deleteRoleTip: '请至少选择一个要删除的角色,不能删除admin/public 角色。',

+ 1 - 0
client/src/i18n/en/user.ts

@@ -12,6 +12,7 @@ const userTrans = {
   newPassword: 'New Password',
   confirmPassword: 'Confirm Password',
   update: 'Update Password',
+  changePassword: 'Change Password',
   isNotSame: 'Not same as new password',
   deleteTip:
     'Please select at least one item to drop and the root user can not be dropped.',

+ 46 - 6
client/src/styles/theme.ts

@@ -1,5 +1,7 @@
 import { PaletteMode } from '@mui/material';
 import { grey } from '@mui/material/colors';
+import { Theme } from '@mui/material/styles';
+import { ButtonProps } from '@mui/material/Button';
 
 declare module '@mui/material/Typography' {
   interface TypographyPropsVariantOverrides {
@@ -126,17 +128,55 @@ export const getAttuTheme = (mode: PaletteMode) => {
       },
       MuiButton: {
         styleOverrides: {
-          root: {
+          root: ({
+            theme,
+            ownerState,
+          }: {
+            theme: Theme;
+            ownerState: ButtonProps;
+          }) => ({
+            padding: theme.spacing(1, 3),
             textTransform: 'initial',
             fontWeight: 'bold',
-            variants: [],
+            ...(ownerState.variant === 'text' && {
+              padding: theme.spacing(1),
+              color: theme.palette.primary.main,
+              '&:hover': {
+                backgroundColor: theme.palette.primary.main,
+                color: theme.palette.background.paper,
+              },
+            }),
+            ...(ownerState.variant === 'contained' && {
+              boxShadow: 'none',
+              '&:hover': {
+                boxShadow: 'none',
+                backgroundColor:
+                  ownerState.color === 'secondary'
+                    ? '#fc4c02'
+                    : theme.palette.primary.dark,
+              },
+            }),
+            ...(ownerState.disabled && {
+              pointerEvents: 'none',
+            }),
+          }),
+        },
+        variants: [
+          {
+            props: { variant: 'contained', color: 'primary' },
+            style: ({ theme }: { theme: Theme }) => ({
+              backgroundColor: theme.palette.primary.main,
+              color: theme.palette.background.paper,
+            }),
           },
-          text: {
-            '&:hover': {
-              backgroundColor: commonThemes.palette.primary.light,
+          {
+            props: { variant: 'contained', color: 'secondary' },
+            style: {
+              backgroundColor: '#fc4c02',
+              color: '#fff',
             },
           },
-        },
+        ],
       },
       MuiDialog: {
         styleOverrides: {