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 Typography from '@mui/material/Typography';
 import Chip from '@mui/material/Chip';
 import Chip from '@mui/material/Chip';
-import { makeStyles } from '@mui/styles';
+import { styled } from '@mui/material/styles';
 import { formatFieldType } from '@/utils';
 import { formatFieldType } from '@/utils';
 import DataView from '@/components/DataView/DataView';
 import DataView from '@/components/DataView/DataView';
 import { DYNAMIC_FIELD } from '@/consts';
 import { DYNAMIC_FIELD } from '@/consts';
 import CopyButton from '@/components/advancedSearch/CopyButton';
 import CopyButton from '@/components/advancedSearch/CopyButton';
-import type { Theme } from '@mui/material/styles';
 import type { CollectionFullObject } from '@server/types';
 import type { CollectionFullObject } from '@server/types';
 
 
 interface DataListViewProps {
 interface DataListViewProps {
   collection: CollectionFullObject;
   collection: CollectionFullObject;
   data: any;
   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) => {
 const DataListView = (props: DataListViewProps) => {
-  // props
   const { collection, data } = props;
   const { collection, data } = props;
-  // styles
-  const classes = useStyles();
 
 
-  // page data
+  // Merge dynamic fields into row
   let row = data[0];
   let row = data[0];
-
-  // merge dymaic fields into row
   row = {
   row = {
     ...row,
     ...row,
     ...row[DYNAMIC_FIELD],
     ...row[DYNAMIC_FIELD],
@@ -80,40 +80,35 @@ const DataListView = (props: DataListViewProps) => {
   }
   }
 
 
   return (
   return (
-    <div className={classes.root}>
+    <Root>
       {Object.keys(row).map((name: string, index: number) => {
       {Object.keys(row).map((name: string, index: number) => {
         const field = collection.schema.fields.find(f => f.name === name);
         const field = collection.schema.fields.find(f => f.name === name);
         return (
         return (
           <div key={index}>
           <div key={index}>
-            <div className={classes.dataTitleContainer}>
-              <span className={classes.title}>
+            <DataTitleContainer>
+              <Title>
                 {name}
                 {name}
-                <CopyButton
-                  className={classes.copy}
-                  value={row[name]}
-                  label={name}
-                />
-              </span>
-              <span className={classes.type}>
+                <StyledCopyButton value={row[name]} label={name} />
+              </Title>
+              <Type>
                 {field && (
                 {field && (
-                  <Chip
-                    className={classes.dataTypeChip}
+                  <DataTypeChip
                     size="small"
                     size="small"
                     label={formatFieldType(field) || 'meta'}
                     label={formatFieldType(field) || 'meta'}
                   />
                   />
                 )}
                 )}
-              </span>
-            </div>
-            <div className={classes.dataContainer}>
+              </Type>
+            </DataTitleContainer>
+            <DataContainer>
               <DataView
               <DataView
                 type={(field && field.data_type) || 'any'}
                 type={(field && field.data_type) || 'any'}
                 value={row[name]}
                 value={row[name]}
               />
               />
-            </div>
+            </DataContainer>
           </div>
           </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 Typography from '@mui/material/Typography';
 import CardContent from '@mui/material/CardContent';
 import CardContent from '@mui/material/CardContent';
-import { makeStyles } from '@mui/styles';
 import StatusIcon, { LoadingType } from '@/components/status/StatusIcon';
 import StatusIcon, { LoadingType } from '@/components/status/StatusIcon';
 import type { FC } from 'react';
 import type { FC } from 'react';
-import type { Theme } from '@mui/material/styles';
 import type { EmptyCardProps } from './Types';
 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> = ({
 const EmptyCard: FC<EmptyCardProps> = ({
   icon,
   icon,
   text,
   text,
   wrapperClass = '',
   wrapperClass = '',
-  subText = '',
   loading = false,
   loading = false,
 }) => {
 }) => {
-  const classes = useStyles();
-
   return (
   return (
-    <section className={`flex-center ${classes.wrapper} ${wrapperClass}`}>
+    <StyledSection className={wrapperClass}>
       <CardContent>
       <CardContent>
         {loading && <StatusIcon type={LoadingType.CREATING} size={40} />}
         {loading && <StatusIcon type={LoadingType.CREATING} size={40} />}
         {icon}
         {icon}
-        <Typography className={classes.text}>{text}</Typography>
-        <Typography className={classes.subText}>{subText}</Typography>
+        <TitleTypography variant="h2">{text}</TitleTypography>
       </CardContent>
       </CardContent>
-    </section>
+    </StyledSection>
   );
   );
 };
 };
 
 

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

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

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

@@ -1,111 +1,38 @@
 import Button from '@mui/material/Button';
 import Button from '@mui/material/Button';
 import Tooltip from '@mui/material/Tooltip';
 import Tooltip from '@mui/material/Tooltip';
-import { makeStyles } from '@mui/styles';
 import type { ButtonProps } from '@mui/material/Button';
 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 Dialog from '@mui/material/Dialog';
 import Typography from '@mui/material/Typography';
 import Typography from '@mui/material/Typography';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
+import { styled } from '@mui/material/styles';
 import CustomButton from '../customButton/CustomButton';
 import CustomButton from '../customButton/CustomButton';
 import CustomDialogTitle from './CustomDialogTitle';
 import CustomDialogTitle from './CustomDialogTitle';
-import { makeStyles } from '@mui/styles';
-import type { Theme } from '@mui/material/styles';
 import type { CustomDialogType } from './Types';
 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 CustomDialog: FC<CustomDialogType> = props => {
   const { t } = useTranslation('btn');
   const { t } = useTranslation('btn');
-  const classes = useStyles();
   const { open, type, params, onClose, containerClass = '' } = props;
   const { open, type, params, onClose, containerClass = '' } = props;
   const {
   const {
     title,
     title,
@@ -41,14 +42,13 @@ const CustomDialog: FC<CustomDialogType> = props => {
     cancelLabel = t('cancel'),
     cancelLabel = t('cancel'),
     confirmClass = '',
     confirmClass = '',
     handleClose,
     handleClose,
-  } = params; // for notice type
-  const { component: CustomComponent } = params; // for custom type
+  } = params;
+  const { component: CustomComponent } = params;
+
   const handleConfirm = async (event: React.FormEvent<HTMLFormElement>) => {
   const handleConfirm = async (event: React.FormEvent<HTMLFormElement>) => {
     if (confirm) {
     if (confirm) {
       const res = await confirm();
       const res = await confirm();
-      if (!res) {
-        return;
-      }
+      if (!res) return;
     }
     }
     handleClose ? handleClose() : onClose();
     handleClose ? handleClose() : onClose();
     event.preventDefault();
     event.preventDefault();
@@ -57,49 +57,30 @@ const CustomDialog: FC<CustomDialogType> = props => {
   const handleCancel = async () => {
   const handleCancel = async () => {
     if (cancel) {
     if (cancel) {
       const res = await cancel();
       const res = await cancel();
-      if (!res) {
-        return;
-      }
+      if (!res) return;
     }
     }
     handleClose ? handleClose() : onClose();
     handleClose ? handleClose() : onClose();
   };
   };
 
 
   return (
   return (
-    <Dialog
-      classes={{
-        paper: `${classes.paper} ${
-          type === 'notice' ? classes.noticePaper : ''
-        }`,
-        paperWidthSm: type === 'notice' ? '' : classes.paperSm,
-        container: `${containerClass}`,
-      }}
+    <StyledDialog
+      type={type}
+      containerClass={containerClass}
       open={open}
       open={open}
       onClose={(event, reason) => {
       onClose={(event, reason) => {
-        if (reason !== 'backdropClick') {
-          handleCancel();
-        }
+        if (reason !== 'backdropClick') handleCancel();
       }}
       }}
     >
     >
       {type === 'notice' ? (
       {type === 'notice' ? (
         <form onSubmit={handleConfirm}>
         <form onSubmit={handleConfirm}>
-          <CustomDialogTitle
-            classes={{ root: classes.title }}
-            onClose={handleCancel}
-          >
+          <CustomDialogTitle onClose={handleCancel}>
             <Typography variant="body1">{title}</Typography>
             <Typography variant="body1">{title}</Typography>
           </CustomDialogTitle>
           </CustomDialogTitle>
-          {component && (
-            <DialogContent classes={{ root: classes.dialogContent }}>
-              {component}
-            </DialogContent>
-          )}
+          {component && <StyledDialogContent>{component}</StyledDialogContent>}
           <DialogActions>
           <DialogActions>
-            <CustomButton
-              onClick={() => handleCancel()}
-              className={classes.cancel}
-            >
+            <StyledCancelButton onClick={handleCancel}>
               {cancelLabel}
               {cancelLabel}
-            </CustomButton>
+            </StyledCancelButton>
             <CustomButton
             <CustomButton
               type="submit"
               type="submit"
               color="primary"
               color="primary"
@@ -113,7 +94,7 @@ const CustomDialog: FC<CustomDialogType> = props => {
       ) : (
       ) : (
         CustomComponent
         CustomComponent
       )}
       )}
-    </Dialog>
+    </StyledDialog>
   );
   );
 };
 };
 
 

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

@@ -1,28 +1,27 @@
 import Typography from '@mui/material/Typography';
 import Typography from '@mui/material/Typography';
 import MuiDialogTitle from '@mui/material/DialogTitle';
 import MuiDialogTitle from '@mui/material/DialogTitle';
 import icons from '../icons/Icons';
 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 { 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 {
 interface IProps extends DialogTitleProps {
@@ -38,24 +37,15 @@ const CustomDialogTitle = (props: IProps) => {
     showCloseIcon = true,
     showCloseIcon = true,
     ...other
     ...other
   } = props;
   } = props;
-  const innerClass = getStyles();
-
-  const ClearIcon = icons.clear;
 
 
   return (
   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 ? (
       {showCloseIcon && onClose ? (
-        <ClearIcon
-          data-testid="clear-icon"
-          classes={{ root: innerClass.icon }}
-          onClick={onClose}
-        />
+        <CloseIcon data-testid="clear-icon" onClick={onClose} />
       ) : null}
       ) : 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 { 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 CustomButton from '@/components/customButton/CustomButton';
 import CustomDialogTitle from '@/components/customDialog/CustomDialogTitle';
 import CustomDialogTitle from '@/components/customDialog/CustomDialogTitle';
 import { rootContext } from '@/context';
 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';
 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',
     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 { handleCloseDialog } = useContext(rootContext);
-  const classes = useStyles();
   const { t: dialogTrans } = useTranslation('dialog');
   const { t: dialogTrans } = useTranslation('dialog');
   const { t: btnTrans } = useTranslation('btn');
   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();
     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 (
   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}
           {title}
-        </CustomDialogTitle>
+        </StyledDialogTitle>
 
 
         <DialogContent>
         <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"
             variant="filled"
-            fullWidth={true}
+            value={value}
+            onChange={(e: ChangeEvent<HTMLInputElement>) =>
+              setValue(e.target.value)
+            }
           />
           />
-          {forceDelLabel ? (
+
+          {forceDelLabel && (
             <FormControlLabel
             <FormControlLabel
               control={
               control={
-                <Checkbox
-                  onChange={(
-                    e: React.ChangeEvent<HTMLInputElement>,
-                    checked: boolean
-                  ) => {
-                    setForce(checked);
-                  }}
-                />
+                <Checkbox onChange={(e, checked) => setForce(checked)} />
               }
               }
-              key={'force'}
               label={forceDelLabel}
               label={forceDelLabel}
-              value={true}
               checked={force}
               checked={force}
-              className={classes.checkBox}
             />
             />
-          ) : null}
+          )}
         </DialogContent>
         </DialogContent>
 
 
-        <DialogActions className={classes.btnWrapper}>
-          <CustomButton
-            name="cancel"
-            onClick={onCancelClick}
-            className={classes.cancelBtn}
-          >
+        <ActionButtons>
+          <CancelButton onClick={handleCancelClick}>
             {btnTrans('cancel')}
             {btnTrans('cancel')}
-          </CustomButton>
+          </CancelButton>
           <CustomButton
           <CustomButton
             type="submit"
             type="submit"
             variant="contained"
             variant="contained"
             color="secondary"
             color="secondary"
             disabled={!deleteReady}
             disabled={!deleteReady}
-            name="delete"
           >
           >
             {label}
             {label}
           </CustomButton>
           </CustomButton>
-        </DialogActions>
+        </ActionButtons>
       </form>
       </form>
-    </div>
+    </Root>
   );
   );
 };
 };
 
 

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

@@ -1,49 +1,54 @@
 import { FC, useRef, useState } from 'react';
 import { FC, useRef, useState } from 'react';
 import { useTranslation } from 'react-i18next';
 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 CustomDialogTitle from './CustomDialogTitle';
 import CustomButton from '../customButton/CustomButton';
 import CustomButton from '../customButton/CustomButton';
 import CodeView from '../code/CodeView';
 import CodeView from '../code/CodeView';
-import { makeStyles } from '@mui/styles';
-import type { Theme } from '@mui/material/styles';
 import type { DialogContainerProps } from './Types';
 import type { DialogContainerProps } from './Types';
 
 
-const useStyles = makeStyles((theme: Theme) => ({
-  wrapper: {
+const Wrapper = styled('section')({
+  display: 'flex',
+  '& form': {
     display: 'flex',
     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,
     color: theme.palette.text.primary,
     backgroundColor: theme.palette.background.paper,
     backgroundColor: theme.palette.background.paper,
-  },
-  dialog: {
     minWidth: 540,
     minWidth: 540,
-  },
-  codeWrapper: {
-    width: (props: { showCode: boolean }) => (props.showCode ? 540 : 0),
     transition: 'width 0.2s',
     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%',
     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,
   showCancel = true,
   showCloseIcon = true,
   showCloseIcon = true,
   leftActions,
   leftActions,
-  // needed for code mode
   showCode = false,
   showCode = false,
   codeBlocksData = [],
   codeBlocksData = [],
   dialogClass = '',
   dialogClass = '',
@@ -69,7 +73,6 @@ const DialogTemplate: FC<DialogContainerProps> = ({
   const { t } = useTranslation('btn');
   const { t } = useTranslation('btn');
   const cancel = cancelLabel || t('cancel');
   const cancel = cancelLabel || t('cancel');
   const confirm = confirmLabel || t('confirm');
   const confirm = confirmLabel || t('confirm');
-  const classes = useStyles({ showCode });
   const onCancel = handleCancel || handleClose;
   const onCancel = handleCancel || handleClose;
 
 
   const dialogRef = useRef(null);
   const dialogRef = useRef(null);
@@ -80,17 +83,19 @@ const DialogTemplate: FC<DialogContainerProps> = ({
     try {
     try {
       await handleConfirm();
       await handleConfirm();
     } catch (error) {
     } catch (error) {
+      console.error(error);
     } finally {
     } finally {
       setConfirming(false);
       setConfirming(false);
     }
     }
   };
   };
 
 
   return (
   return (
-    <section className={classes.wrapper}>
+    <Wrapper>
       <form onSubmit={_handleConfirm}>
       <form onSubmit={_handleConfirm}>
-        <div
+        <DialogBlock
+          showCode={showCode}
           ref={dialogRef}
           ref={dialogRef}
-          className={`${classes.dialog} ${classes.block} ${dialogClass}`}
+          className={dialogClass}
         >
         >
           <CustomDialogTitle
           <CustomDialogTitle
             onClose={handleClose}
             onClose={handleClose}
@@ -100,7 +105,7 @@ const DialogTemplate: FC<DialogContainerProps> = ({
           </CustomDialogTitle>
           </CustomDialogTitle>
           <DialogContent>{children}</DialogContent>
           <DialogContent>{children}</DialogContent>
           {showActions && (
           {showActions && (
-            <DialogActions className={classes.actions}>
+            <Actions>
               <div>{leftActions}</div>
               <div>{leftActions}</div>
               <div>
               <div>
                 {showCancel && (
                 {showCancel && (
@@ -124,17 +129,19 @@ const DialogTemplate: FC<DialogContainerProps> = ({
                   {confirming ? <CircularProgress size={16} /> : confirm}
                   {confirming ? <CircularProgress size={16} /> : confirm}
                 </CustomButton>
                 </CustomButton>
               </div>
               </div>
-            </DialogActions>
+            </Actions>
           )}
           )}
-        </div>
+        </DialogBlock>
 
 
-        <div className={`${classes.block} ${classes.codeWrapper}`}>
+        <CodeWrapper showCode={showCode}>
           {showCode && (
           {showCode && (
-            <CodeView wrapperClass={classes.code} data={codeBlocksData} />
+            <CodeContainer showCode={showCode}>
+              <CodeView data={codeBlocksData} />
+            </CodeContainer>
           )}
           )}
-        </div>
+        </CodeWrapper>
       </form>
       </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 InputAdornment from '@mui/material/InputAdornment';
 import InputLabel from '@mui/material/InputLabel';
 import InputLabel from '@mui/material/InputLabel';
 import TextField from '@mui/material/TextField';
 import TextField from '@mui/material/TextField';
-import { makeStyles } from '@mui/styles';
 import Icons from '../icons/Icons';
 import Icons from '../icons/Icons';
 import { ReactElement } from 'react';
 import { ReactElement } from 'react';
+import { styled } from '@mui/material/styles';
 import type { FilledTextFieldProps } from '@mui/material/TextField';
 import type { FilledTextFieldProps } from '@mui/material/TextField';
 import type { StandardTextFieldProps } from '@mui/material/TextField';
 import type { StandardTextFieldProps } from '@mui/material/TextField';
 import type { Theme } from '@mui/material/styles';
 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 = (
 const getAdornmentInput = (
@@ -93,8 +91,6 @@ const getAdornmentInput = (
     onInputChange,
     onInputChange,
   } = config;
   } = config;
 
 
-  const classes = getAdornmentStyles();
-
   const param = {
   const param = {
     cb: onInputBlur || (() => {}),
     cb: onInputBlur || (() => {}),
     validations: validations || [],
     validations: validations || [],
@@ -126,7 +122,7 @@ const getAdornmentInput = (
         }}
         }}
         endAdornment={
         endAdornment={
           <InputAdornment position="end">
           <InputAdornment position="end">
-            <IconButton
+            <StyledIconButton
               onClick={onIconClick || (() => {})}
               onClick={onIconClick || (() => {})}
               edge="end"
               edge="end"
               role="icon-button"
               role="icon-button"
@@ -134,10 +130,10 @@ const getAdornmentInput = (
             >
             >
               {isPasswordType
               {isPasswordType
                 ? showPassword
                 ? showPassword
-                  ? Icons.visible({ classes: { root: classes.icon } })
-                  : Icons.invisible({ classes: { root: classes.icon } })
+                  ? Icons.visible()
+                  : Icons.invisible()
                 : icon}
                 : icon}
-            </IconButton>
+            </StyledIconButton>
           </InputAdornment>
           </InputAdornment>
         }
         }
         inputProps={{
         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 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) => {
 const CustomInput = (props: ICustomInputProps) => {

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

@@ -1,51 +1,46 @@
 import InputAdornment from '@mui/material/InputAdornment';
 import InputAdornment from '@mui/material/InputAdornment';
 import TextField from '@mui/material/TextField';
 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 { useRef, FC, useState, useEffect, useMemo } from 'react';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 import Icons from '../icons/Icons';
 import Icons from '../icons/Icons';
-import type { Theme } from '@mui/material/styles';
 import type { SearchType } from './Types';
 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,
     padding: 0,
     height: '16px',
     height: '16px',
 
 
@@ -53,34 +48,42 @@ const useSearchStyles = makeStyles((theme: Theme) => ({
       caretColor: theme.palette.primary.main,
       caretColor: theme.palette.primary.main,
     },
     },
   },
   },
-  searchIcon: {
+}));
+
+const SearchIconWrapper = styled('span')<{ searched?: boolean }>(
+  ({ theme, searched }) => ({
     color: theme.palette.text.secondary,
     color: theme.palette.text.secondary,
     cursor: 'pointer',
     cursor: 'pointer',
     fontSize: '20px',
     fontSize: '20px',
-    width: (props: { searched: boolean }) => `${props.searched ? 0 : '20px'}`,
-
+    width: searched ? 0 : '20px',
     transition: 'width 0.2s',
     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',
     transition: 'opacity 0.2s',
-  },
-  searchWrapper: {
     display: 'flex',
     display: 'flex',
     justifyContent: 'center',
     justifyContent: 'center',
     alignItems: 'center',
     alignItems: 'center',
-  },
+  })
+);
+
+const ClearIcon = styled(Icons.clear)(({ theme }) => ({
+  color: theme.palette.primary.main,
+  cursor: 'pointer',
 }));
 }));
 
 
 const SearchInput: FC<SearchType> = props => {
 const SearchInput: FC<SearchType> = props => {
   const { searchText = '', onClear = () => {}, onSearch = () => {} } = props;
   const { searchText = '', onClear = () => {}, onSearch = () => {} } = props;
   const [searchValue, setSearchValue] = useState<string>(searchText || '');
   const [searchValue, setSearchValue] = useState<string>(searchText || '');
   const searched = useMemo(() => searchValue !== '', [searchValue]);
   const searched = useMemo(() => searchValue !== '', [searchValue]);
-  const classes = useSearchStyles({ searched });
   const { t: commonTrans } = useTranslation();
   const { t: commonTrans } = useTranslation();
   const inputRef = useRef<any>(null);
   const inputRef = useRef<any>(null);
 
 
@@ -90,67 +93,60 @@ const SearchInput: FC<SearchType> = props => {
 
 
   useEffect(() => {
   useEffect(() => {
     let hashPart = window.location.hash.substring(1);
     let hashPart = window.location.hash.substring(1);
-    // remove search part from hash part, include the '?'
     hashPart = hashPart.replace(/(\?search=)[^&]+(&?)/, '$2');
     hashPart = hashPart.replace(/(\?search=)[^&]+(&?)/, '$2');
-
-    let searchPart = !searchValue
+    const searchPart = !searchValue
       ? ''
       ? ''
       : `?search=${encodeURIComponent(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);
     handleSearch(searchValue);
   }, [searchValue]);
   }, [searchValue]);
 
 
   return (
   return (
-    <div className={classes.wrapper}>
-      <TextField
+    <SearchWrapper>
+      <StyledTextField
+        searched={searched}
         inputRef={inputRef}
         inputRef={inputRef}
         variant="standard"
         variant="standard"
-        classes={{ root: classes.input }}
         InputProps={{
         InputProps={{
           disableUnderline: true,
           disableUnderline: true,
-          classes: { input: classes.textfield },
+          startAdornment: (
+            <InputAdornment position="start">
+              <SearchIconWrapper
+                className="search-icon"
+                searched={searched}
+                onClick={() => handleSearch(searchValue)}
+              >
+                {Icons.search()}
+              </SearchIconWrapper>
+            </InputAdornment>
+          ),
           endAdornment: (
           endAdornment: (
             <InputAdornment position="end">
             <InputAdornment position="end">
-              <span
+              <IconWrapper
                 data-testid="clear-icon"
                 data-testid="clear-icon"
-                className={`flex-center ${classes.iconWrapper}`}
-                onClick={e => {
+                searched={searched}
+                onClick={() => {
                   setSearchValue('');
                   setSearchValue('');
-                  inputRef.current.focus();
+                  inputRef.current?.focus();
                   onClear();
                   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>
             </InputAdornment>
           ),
           ),
         }}
         }}
         onChange={e => {
         onChange={e => {
           const value = e.target.value.trim();
           const value = e.target.value.trim();
           setSearchValue(value);
           setSearchValue(value);
-          if (value === '') {
-            onClear();
-          }
+          if (value === '') onClear();
         }}
         }}
         onKeyPress={e => {
         onKeyPress={e => {
           if (e.key === 'Enter') {
           if (e.key === 'Enter') {
-            // Do code here
             handleSearch(searchValue);
             handleSearch(searchValue);
             e.preventDefault();
             e.preventDefault();
           }
           }
@@ -158,7 +154,7 @@ const SearchInput: FC<SearchType> = props => {
         value={searchValue || ''}
         value={searchValue || ''}
         placeholder={commonTrans('search')}
         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 ListSubheader from '@mui/material/ListSubheader';
 import MenuItem from '@mui/material/MenuItem';
 import MenuItem from '@mui/material/MenuItem';
 import Select from '@mui/material/Select';
 import Select from '@mui/material/Select';
-import { makeStyles } from '@mui/styles';
+import { styled } from '@mui/material/styles';
 import { FC } from 'react';
 import { FC } from 'react';
-import type { Theme } from '@mui/material/styles';
 import type { GroupOption, ICustomGroupSelect } from './Types';
 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)',
     backgroundColor: 'rgba(0, 0, 0, 0.03)',
   },
   },
 }));
 }));
 
 
 const CustomGroupedSelect: FC<ICustomGroupSelect> = props => {
 const CustomGroupedSelect: FC<ICustomGroupSelect> = props => {
-  const classes = getStyles();
   const {
   const {
     options,
     options,
     className = '',
     className = '',
@@ -51,26 +51,17 @@ const CustomGroupedSelect: FC<ICustomGroupSelect> = props => {
   const renderSelectGroup = (option: GroupOption) => {
   const renderSelectGroup = (option: GroupOption) => {
     const items = option.children.map(child => {
     const items = option.children.map(child => {
       return (
       return (
-        <MenuItem
-          classes={{ root: classes.menuItem }}
-          key={child.value}
-          value={child.value}
-        >
+        <StyledMenuItem key={child.value} value={child.value}>
           {child.label}
           {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 (
   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>}
         {haveLabel && <InputLabel htmlFor="grouped-select">{label}</InputLabel>}
         <Select
         <Select
           displayEmpty={!haveLabel}
           displayEmpty={!haveLabel}
@@ -91,8 +82,8 @@ const CustomGroupedSelect: FC<ICustomGroupSelect> = props => {
         >
         >
           {options.map(option => renderSelectGroup(option))}
           {options.map(option => renderSelectGroup(option))}
         </Select>
         </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 MenuItem from '@mui/material/MenuItem';
 import Select from '@mui/material/Select';
 import Select from '@mui/material/Select';
 import Checkbox from '@mui/material/Checkbox';
 import Checkbox from '@mui/material/Checkbox';
-import { withStyles } from '@mui/styles';
+import { styled } from '@mui/material/styles';
 import { generateId } from '../../utils/Common';
 import { generateId } from '../../utils/Common';
 import type { CustomMultiSelectorType } from './Types';
 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 CustomSelector: FC<CustomMultiSelectorType> = props => {
   const {
   const {
@@ -36,12 +34,12 @@ const CustomSelector: FC<CustomMultiSelectorType> = props => {
   return (
   return (
     <FormControl variant={variant} className={wrapperClass} size={size}>
     <FormControl variant={variant} className={wrapperClass} size={size}>
       {label && (
       {label && (
-        <InputLabel classes={{ root: labelClass }} htmlFor={id}>
+        <InputLabel className={labelClass} htmlFor={id}>
           {label}
           {label}
         </InputLabel>
         </InputLabel>
       )}
       )}
       <Select
       <Select
-        classes={{ ...classes }}
+        className={classes?.root}
         {...others}
         {...others}
         multiple={true}
         multiple={true}
         value={values}
         value={values}
@@ -52,10 +50,10 @@ const CustomSelector: FC<CustomMultiSelectorType> = props => {
         renderValue={renderValue}
         renderValue={renderValue}
       >
       >
         {options.map(v => (
         {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} />
             <Checkbox checked={values.indexOf(v.value as string) !== -1} />
             {v.label}
             {v.label}
-          </CustomMenuItem>
+          </StyledMenuItem>
         ))}
         ))}
       </Select>
       </Select>
     </FormControl>
     </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 MuiAlert from '@mui/material/Alert';
 import Snackbar from '@mui/material/Snackbar';
 import Snackbar from '@mui/material/Snackbar';
 import Slide from '@mui/material/Slide';
 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 { AlertProps } from '@mui/material/Alert';
 import type { SlideProps } from '@mui/material/Slide';
 import type { SlideProps } from '@mui/material/Slide';
 import type { CustomSnackBarType } from './Types';
 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" />;
   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')]: {
     [theme.breakpoints.up('md')]: {
       top: '72px',
       top: '72px',
       right: theme.spacing(4),
       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 CustomSnackBar: FC<CustomSnackBarType> = props => {
   const {
   const {
     vertical,
     vertical,
@@ -49,14 +45,14 @@ const CustomSnackBar: FC<CustomSnackBarType> = props => {
     message,
     message,
     onClose,
     onClose,
   } = props;
   } = props;
-  const classes = useStyles();
+
   const handleClose = (event: React.SyntheticEvent<any> | Event) => {
   const handleClose = (event: React.SyntheticEvent<any> | Event) => {
     onClose && onClose();
     onClose && onClose();
   };
   };
 
 
   return (
   return (
     <div>
     <div>
-      <Snackbar
+      <StyledSnackbar
         anchorOrigin={{
         anchorOrigin={{
           vertical: vertical,
           vertical: vertical,
           horizontal: horizontal,
           horizontal: horizontal,
@@ -65,19 +61,12 @@ const CustomSnackBar: FC<CustomSnackBarType> = props => {
         open={open}
         open={open}
         onClose={handleClose}
         onClose={handleClose}
         autoHideDuration={autoHideDuration}
         autoHideDuration={autoHideDuration}
-        classes={{
-          anchorOriginTopRight: classes.topRight,
-        }}
         TransitionComponent={SlideTransition}
         TransitionComponent={SlideTransition}
       >
       >
-        <Alert
-          onClose={handleClose}
-          severity={type}
-          classes={{ root: classes.root }}
-        >
+        <StyledAlert onClose={handleClose} severity={type}>
           {message}
           {message}
-        </Alert>
-      </Snackbar>
+        </StyledAlert>
+      </StyledSnackbar>
     </div>
     </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 Tooltip from '@mui/material/Tooltip';
 import { FC } from 'react';
 import { FC } from 'react';
-import { makeStyles } from '@mui/styles';
+import { styled } from '@mui/material/styles';
 import type { Theme } from '@mui/material/styles';
 import type { Theme } from '@mui/material/styles';
 import type { CustomToolTipType } from './Types';
 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 CustomToolTip: FC<CustomToolTipType> = props => {
-  const classes = useStyles();
   const { title, placement = 'right', children, leaveDelay = 0 } = props;
   const { title, placement = 'right', children, leaveDelay = 0 } = props;
   return (
   return (
-    <Tooltip
-      classes={{ tooltip: classes.tooltip }}
+    <StyledTooltip
       leaveDelay={leaveDelay}
       leaveDelay={leaveDelay}
       title={title}
       title={title}
       placement={placement}
       placement={placement}
       arrow
       arrow
     >
     >
       <span>{children}</span>
       <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 IconButton from '@mui/material/IconButton';
 import Button from '@mui/material/Button';
 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 Icons from '../icons/Icons';
 import CustomToolTip from '../customToolTip/CustomToolTip';
 import CustomToolTip from '../customToolTip/CustomToolTip';
-import type { Theme } from '@mui/material/styles';
 import type { ActionBarType } from './Types';
 import type { ActionBarType } from './Types';
 
 
-const useStyles = makeStyles((theme: Theme) => ({
-  root: {
+const Root = styled('span')<{ isHoverType: boolean }>(
+  ({ theme, isHoverType }) => ({
     position: 'relative',
     position: 'relative',
     display: 'inline-block',
     display: 'inline-block',
     marginRight: theme.spacing(1),
     marginRight: theme.spacing(1),
     color: theme.palette.text.primary,
     color: theme.palette.text.primary,
     backgroundColor: theme.palette.background.paper,
     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,
     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 ActionBar: FC<ActionBarType> = props => {
-  const classes = useStyles();
   const { configs, row, isHoverType = false } = props;
   const { configs, row, isHoverType = false } = props;
 
 
   return (
   return (
@@ -51,60 +43,37 @@ const ActionBar: FC<ActionBarType> = props => {
         const label = v.getLabel ? v.getLabel(row) : v.label;
         const label = v.getLabel ? v.getLabel(row) : v.label;
 
 
         return (
         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">
             <CustomToolTip title={label || ''} placement="bottom">
               {v.icon ? (
               {v.icon ? (
-                <IconButton
+                <DisabledIconButton
                   aria-label={label || ''}
                   aria-label={label || ''}
-                  onClickCapture={e => {
+                  onClickCapture={(e: MouseEvent<HTMLButtonElement>) => {
                     e.stopPropagation();
                     e.stopPropagation();
                     v.onClick(e, row);
                     v.onClick(e, row);
                   }}
                   }}
                   disabled={v.disabled ? v.disabled(row) : false}
                   disabled={v.disabled ? v.disabled(row) : false}
-                  classes={{
-                    disabled: classes.disabled,
-                  }}
                   size="large"
                   size="large"
                 >
                 >
                   {v.showIconMethod === 'renderFn'
                   {v.showIconMethod === 'renderFn'
                     ? v.renderIconFn && v.renderIconFn(row)
                     ? v.renderIconFn && v.renderIconFn(row)
                     : Icons[v.icon]()}
                     : 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 || ''}
                   aria-label={label || ''}
-                  onClickCapture={e => {
+                  onClickCapture={(e: MouseEvent<HTMLButtonElement>) => {
                     e.stopPropagation();
                     e.stopPropagation();
                     v.onClick(e, row);
                     v.onClick(e, row);
                   }}
                   }}
                   size="small"
                   size="small"
                   disabled={v.disabled ? v.disabled(row) : false}
                   disabled={v.disabled ? v.disabled(row) : false}
-                  classes={{
-                    disabled: classes.disabled,
-                  }}
                 >
                 >
                   {v.text}
                   {v.text}
-                </Button>
+                </DisabledButton>
               )}
               )}
             </CustomToolTip>
             </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 { useTranslation } from 'react-i18next';
 import Typography from '@mui/material/Typography';
 import Typography from '@mui/material/Typography';
 import Grid from '@mui/material/Grid';
 import Grid from '@mui/material/Grid';
@@ -12,6 +12,13 @@ import type { Theme } from '@mui/material/styles/createTheme';
 import type { AttuGridType } from './Types';
 import type { AttuGridType } from './Types';
 
 
 const userStyle = makeStyles((theme: Theme) => ({
 const userStyle = makeStyles((theme: Theme) => ({
+  wrapper: {
+    height: '100%',
+  },
+  container: {
+    flexWrap: 'nowrap',
+    flexDirection: 'column',
+  },
   loading: {
   loading: {
     height: '100%',
     height: '100%',
     display: 'flex',
     display: 'flex',
@@ -20,22 +27,11 @@ const userStyle = makeStyles((theme: Theme) => ({
     padding: theme.spacing(20),
     padding: theme.spacing(20),
     width: '100%',
     width: '100%',
   },
   },
-
   tableTitle: {
   tableTitle: {
     '& .last': {
     '& .last': {
       color: 'rgba(0, 0, 0, 0.54)',
       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: {
   pagenation: {
     '& .MuiTablePagination-spacer': {
     '& .MuiTablePagination-spacer': {
       display: 'none',
       display: 'none',
@@ -58,21 +54,11 @@ const userStyle = makeStyles((theme: Theme) => ({
       },
       },
     },
     },
   },
   },
-
   noBottomPadding: {
   noBottomPadding: {
     paddingBottom: '0 !important',
     paddingBottom: '0 !important',
     display: 'flex',
     display: 'flex',
     flexDirection: 'column',
     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 AttuGrid: FC<AttuGridType> = props => {
   const classes = userStyle();
   const classes = userStyle();
   const tableRef = useRef<HTMLDivElement | null>(null);
   const tableRef = useRef<HTMLDivElement | null>(null);
-  const [loadingRowCount, setLoadingRowCount] = useState<number>(0);
 
 
   // i18n
   // i18n
   const { t: commonTrans } = useTranslation();
   const { t: commonTrans } = useTranslation();
@@ -200,9 +185,6 @@ const AttuGrid: FC<AttuGridType> = props => {
 
 
       const rowCount = Math.floor(totalHeight / rowHeight);
       const rowCount = Math.floor(totalHeight / rowHeight);
 
 
-      // fix loading mask
-      setLoadingRowCount(rowCount);
-
       if (setRowsPerPage) {
       if (setRowsPerPage) {
         setRowsPerPage(rowCount);
         setRowsPerPage(rowCount);
       }
       }
@@ -261,7 +243,6 @@ const AttuGrid: FC<AttuGridType> = props => {
 
 
       <Grid item xs={12} className={classes.noBottomPadding}>
       <Grid item xs={12} className={classes.noBottomPadding}>
         <Table
         <Table
-          loadingRowCount={loadingRowCount}
           openCheckBox={openCheckBox}
           openCheckBox={openCheckBox}
           primaryKey={primaryKey}
           primaryKey={primaryKey}
           rows={rows}
           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 Box from '@mui/material/Box';
 import { useTranslation } from 'react-i18next';
 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');
   const { t: btnTrans } = useTranslation('btn');
 
 
   return (
   return (

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

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

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

@@ -2,37 +2,27 @@ import { FC } from 'react';
 import TableHead from '@mui/material/TableHead';
 import TableHead from '@mui/material/TableHead';
 import TableRow from '@mui/material/TableRow';
 import TableRow from '@mui/material/TableRow';
 import TableCell from '@mui/material/TableCell';
 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';
 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 EditableTableHead: FC<TableEditableHeadType> = props => {
   const { editHeads } = props;
   const { editHeads } = props;
-  const classes = useStyles();
 
 
   return (
   return (
     <TableHead>
     <TableHead>
-      <TableRow className={classes.tableRow}>
+      <StyledTableRow>
         {editHeads.map((headCell, index) => (
         {editHeads.map((headCell, index) => (
-          <TableCell key={index} className={classes.tableCell}>
-            {headCell.component}
-          </TableCell>
+          <StyledTableCell key={index}>{headCell.component}</StyledTableCell>
         ))}
         ))}
-      </TableRow>
+      </StyledTableRow>
     </TableHead>
     </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 Checkbox from '@mui/material/Checkbox';
 import TableSortLabel from '@mui/material/TableSortLabel';
 import TableSortLabel from '@mui/material/TableSortLabel';
 import Typography from '@mui/material/Typography';
 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';
 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 => {
 const EnhancedTableHead: FC<TableHeadType> = props => {
@@ -51,7 +49,7 @@ const EnhancedTableHead: FC<TableHeadType> = props => {
     openCheckBox,
     openCheckBox,
     disableSelect,
     disableSelect,
   } = props;
   } = props;
-  const classes = useStyles();
+
   const createSortHandler = (property: string) => (event: React.MouseEvent) => {
   const createSortHandler = (property: string) => (event: React.MouseEvent) => {
     handleSort &&
     handleSort &&
       handleSort(
       handleSort(
@@ -63,7 +61,7 @@ const EnhancedTableHead: FC<TableHeadType> = props => {
 
 
   return (
   return (
     <TableHead>
     <TableHead>
-      <TableRow className={classes.tableRow}>
+      <StyledTableRow>
         {openCheckBox && (
         {openCheckBox && (
           <TableCell padding="checkbox" role="cell">
           <TableCell padding="checkbox" role="cell">
             <Checkbox
             <Checkbox
@@ -91,7 +89,7 @@ const EnhancedTableHead: FC<TableHeadType> = props => {
             headCell.headerFormatter || (v => <>{v.label}</>);
             headCell.headerFormatter || (v => <>{v.label}</>);
 
 
           return (
           return (
-            <TableCell
+            <StyledTableCell
               key={headCell.id + headCell.label}
               key={headCell.id + headCell.label}
               align={headCell.align || 'left'}
               align={headCell.align || 'left'}
               padding={headCell.disablePadding ? 'none' : 'normal'}
               padding={headCell.disablePadding ? 'none' : 'normal'}
@@ -99,7 +97,6 @@ const EnhancedTableHead: FC<TableHeadType> = props => {
                 orderBy === (headCell.sortBy || headCell.id) ? order : false
                 orderBy === (headCell.sortBy || headCell.id) ? order : false
               }
               }
               style={cellStyle}
               style={cellStyle}
-              className={classes.tableCell}
               role="cell"
               role="cell"
             >
             >
               {headCell.label && handleSort && !headCell.notSort ? (
               {headCell.label && handleSort && !headCell.notSort ? (
@@ -110,27 +107,27 @@ const EnhancedTableHead: FC<TableHeadType> = props => {
                   }
                   }
                   onClick={createSortHandler(headCell.sortBy || headCell.id)}
                   onClick={createSortHandler(headCell.sortBy || headCell.id)}
                 >
                 >
-                  <Typography variant="body1" className={classes.tableHeader}>
+                  <StyledTableHeader variant="body1">
                     {headerFormatter(headCell)}
                     {headerFormatter(headCell)}
-                  </Typography>
+                  </StyledTableHeader>
 
 
                   {orderBy === (headCell.sortBy || headCell.id) ? (
                   {orderBy === (headCell.sortBy || headCell.id) ? (
-                    <Typography className={classes.visuallyHidden}>
+                    <VisuallyHiddenTypography>
                       {order === 'desc'
                       {order === 'desc'
                         ? 'sorted descending'
                         ? 'sorted descending'
                         : 'sorted ascending'}
                         : 'sorted ascending'}
-                    </Typography>
+                    </VisuallyHiddenTypography>
                   ) : null}
                   ) : null}
                 </TableSortLabel>
                 </TableSortLabel>
               ) : (
               ) : (
-                <Typography variant="body1" className={classes.tableHeader}>
+                <StyledTableHeader variant="body1">
                   {headerFormatter(headCell)}
                   {headerFormatter(headCell)}
-                </Typography>
+                </StyledTableHeader>
               )}
               )}
-            </TableCell>
+            </StyledTableCell>
           );
           );
         })}
         })}
-      </TableRow>
+      </StyledTableRow>
     </TableHead>
     </TableHead>
   );
   );
 };
 };

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

@@ -3,32 +3,30 @@ import CustomButton from '../customButton/CustomButton';
 import icons from '../icons/Icons';
 import icons from '../icons/Icons';
 import React from 'react';
 import React from 'react';
 import { useTranslation } from 'react-i18next';
 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';
 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 TablePaginationActions = (props: TablePaginationActionsProps) => {
-  const classes = useStyles();
   const { count, page, rowsPerPage, onPageChange } = props;
   const { count, page, rowsPerPage, onPageChange } = props;
 
 
   // icons
   // icons
@@ -52,27 +50,23 @@ const TablePaginationActions = (props: TablePaginationActionsProps) => {
   };
   };
 
 
   return (
   return (
-    <div className={classes.root}>
-      <CustomButton
+    <Root>
+      <StyledButton
         onClick={handleBackButtonClick}
         onClick={handleBackButtonClick}
         disabled={page === 0}
         disabled={page === 0}
         aria-label={gridTrans.prevLabel}
         aria-label={gridTrans.prevLabel}
-        className={classes.btn}
       >
       >
         <PrevIcon />
         <PrevIcon />
-      </CustomButton>
-      <Typography variant="body2" className={classes.page}>
-        {page + 1}
-      </Typography>
-      <CustomButton
+      </StyledButton>
+      <PageNumber variant="body2">{page + 1}</PageNumber>
+      <StyledButton
         onClick={handleNextButtonClick}
         onClick={handleNextButtonClick}
         disabled={page >= Math.ceil(count / rowsPerPage) - 1}
         disabled={page >= Math.ceil(count / rowsPerPage) - 1}
         aria-label={gridTrans.nextLabel}
         aria-label={gridTrans.nextLabel}
-        className={classes.btn}
       >
       >
         <NextIcon />
         <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 CustomButton from '../customButton/CustomButton';
 import Icons from '../icons/Icons';
 import Icons from '../icons/Icons';
 import SearchInput from '../customInput/SearchInput';
 import SearchInput from '../customInput/SearchInput';
-import TableSwitch from './TableSwitch';
 import { throwErrorForDev } from '../../utils/Common';
 import { throwErrorForDev } from '../../utils/Common';
 import CustomIconButton from '../customButton/CustomIconButton';
 import CustomIconButton from '../customButton/CustomIconButton';
 import { makeStyles } from '@mui/styles';
 import { makeStyles } from '@mui/styles';
@@ -136,19 +135,6 @@ const CustomToolBar: FC<ToolBarType> = props => {
                 );
                 );
               }
               }
               switch (c.type) {
               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 'select':
                 case 'groupSelect':
                 case 'groupSelect':
                   if (!c.component) {
                   if (!c.component) {

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

@@ -95,7 +95,6 @@ export type TableType = {
   handleSort?: (e: any, orderBy: string) => void;
   handleSort?: (e: any, orderBy: string) => void;
   order?: SortDirection;
   order?: SortDirection;
   orderBy?: string;
   orderBy?: string;
-  loadingRowCount: number;
   ref?: Ref<HTMLDivElement>;
   ref?: Ref<HTMLDivElement>;
 };
 };
 
 
@@ -167,7 +166,6 @@ type ActionBarConfig = {
   onClick: (e: React.MouseEvent, row: any) => void;
   onClick: (e: React.MouseEvent, row: any) => void;
   icon?: IconsType;
   icon?: IconsType;
   text?: string;
   text?: string;
-  linkButton?: boolean;
   showIconMethod?: 'iconType' | 'renderFn';
   showIconMethod?: 'iconType' | 'renderFn';
   renderIconFn?: (row: any) => ReactElement;
   renderIconFn?: (row: any) => ReactElement;
   label?: string;
   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 StatusIcon from '@/components/status/StatusIcon';
 import UpdateUser from '@/pages/user/dialogs/UpdateUserPassDialog';
 import UpdateUser from '@/pages/user/dialogs/UpdateUserPassDialog';
 import icons from '../icons/Icons';
 import icons from '../icons/Icons';
-import { makeStyles } from '@mui/styles';
+import { styled } from '@mui/material/styles';
 import IconButton from '@mui/material/IconButton';
 import IconButton from '@mui/material/IconButton';
 import { ColorModeContext } from '@/context';
 import { ColorModeContext } from '@/context';
 import { LoadingType } from '@/components/status/StatusIcon';
 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,
     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 = () => {
 const Header: FC = () => {
-  // styles
-  const classes = useStyles();
   // use context
   // use context
   const { navInfo } = useContext(navContext);
   const { navInfo } = useContext(navContext);
   const { mode, toggleColorMode } = useContext(ColorModeContext);
   const { mode, toggleColorMode } = useContext(ColorModeContext);
@@ -112,6 +114,7 @@ const Header: FC = () => {
   const statusTrans = commonTrans('status');
   const statusTrans = commonTrans('status');
   const { t: dbTrans } = useTranslation('database');
   const { t: dbTrans } = useTranslation('database');
   const { t: successTrans } = useTranslation('success');
   const { t: successTrans } = useTranslation('success');
+  const { t: userTrans } = useTranslation('user');
 
 
   // icons
   // icons
   const BackIcon = icons.back;
   const BackIcon = icons.back;
@@ -170,18 +173,17 @@ const Header: FC = () => {
   const isLoadingDb = dbOptions.length === 0;
   const isLoadingDb = dbOptions.length === 0;
 
 
   return (
   return (
-    <header className={classes.header}>
-      <div className={classes.contentWrapper}>
-        <div className={classes.navigation}>
+    <HeaderWrapper>
+      <ContentWrapper>
+        <Navigation>
           {navInfo.backPath !== '' && (
           {navInfo.backPath !== '' && (
-            <BackIcon
-              classes={{ root: classes.icon }}
-              onClick={() => handleBack(navInfo.backPath)}
-            />
+            <StyledIcon onClick={() => handleBack(navInfo.backPath)}>
+              <BackIcon />
+            </StyledIcon>
           )}
           )}
           {navInfo.showDatabaseSelector &&
           {navInfo.showDatabaseSelector &&
             (!isLoadingDb ? (
             (!isLoadingDb ? (
-              <CustomSelector
+              <DatabaseSelector
                 label={dbTrans('database')}
                 label={dbTrans('database')}
                 value={database}
                 value={database}
                 onChange={async (e: { target: { value: unknown } }) => {
                 onChange={async (e: { target: { value: unknown } }) => {
@@ -196,31 +198,22 @@ const Header: FC = () => {
                 }}
                 }}
                 options={dbOptions}
                 options={dbOptions}
                 variant="filled"
                 variant="filled"
-                wrapperClass={classes.database}
                 disabled={loading}
                 disabled={loading}
               />
               />
             ) : (
             ) : (
               <StatusIcon type={LoadingType.CREATING} />
               <StatusIcon type={LoadingType.CREATING} />
             ))}
             ))}
 
 
-          <Typography
-            variant="h5"
-            color="textPrimary"
-            className={classes.title}
-          >
+          <Title variant="h5" color="textPrimary">
             {navInfo.navTitle}
             {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 />}
             {mode === 'dark' ? <icons.night /> : <icons.day />}
-          </IconButton>
+          </ModeButton>
           <div className="text">
           <div className="text">
             <Typography className="address">{address}</Typography>
             <Typography className="address">{address}</Typography>
             <Typography className="status">{statusTrans.running}</Typography>
             <Typography className="status">{statusTrans.running}</Typography>
@@ -228,12 +221,12 @@ const Header: FC = () => {
           {username && (
           {username && (
             <>
             <>
               <Tooltip title={username}>
               <Tooltip title={username}>
-                <div
+                <StyledIcon
                   onClick={handleUserMenuClick}
                   onClick={handleUserMenuClick}
                   style={{ cursor: 'pointer' }}
                   style={{ cursor: 'pointer' }}
                 >
                 >
-                  <Avatar classes={{ root: classes.icon }} />
-                </div>
+                  <Avatar />
+                </StyledIcon>
               </Tooltip>
               </Tooltip>
               <Menu
               <Menu
                 anchorEl={anchorEl}
                 anchorEl={anchorEl}
@@ -249,22 +242,19 @@ const Header: FC = () => {
                 }}
                 }}
               >
               >
                 <MenuItem onClick={handleChangePassword}>
                 <MenuItem onClick={handleChangePassword}>
-                  Change Password
+                  {userTrans('changePassword')}
                 </MenuItem>
                 </MenuItem>
               </Menu>
               </Menu>
             </>
             </>
           )}
           )}
           <Tooltip title={'disconnect'}>
           <Tooltip title={'disconnect'}>
-            <div>
-              <LogoutIcon
-                classes={{ root: classes.icon }}
-                onClick={handleLogout}
-              />
-            </div>
+            <StyledIcon>
+              <LogoutIcon onClick={handleLogout} />
+            </StyledIcon>
           </Tooltip>
           </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 { useTranslation } from 'react-i18next';
+import { styled } from '@mui/material/styles';
 import type { Theme } from '@mui/material/styles';
 import type { Theme } from '@mui/material/styles';
 
 
 interface WrapperProps {
 interface WrapperProps {
@@ -8,30 +8,30 @@ interface WrapperProps {
   className?: string;
   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 = ({
 const Wrapper = ({
@@ -39,23 +39,18 @@ const Wrapper = ({
   children,
   children,
   className,
   className,
 }: WrapperProps) => {
 }: WrapperProps) => {
-  // styles
-  const classes = useStyles();
-
   // i18n
   // i18n
   const { t: commonTrans } = useTranslation();
   const { t: commonTrans } = useTranslation();
 
 
   return (
   return (
-    <div className={`${classes.wrapper} ${className}`}>
+    <WrapperRoot className={className}>
       {children}
       {children}
       {!hasPermission && (
       {!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 { useTranslation } from 'react-i18next';
 import Typography from '@mui/material/Typography';
 import Typography from '@mui/material/Typography';
 import { useTheme } from '@mui/material/styles';
 import { useTheme } from '@mui/material/styles';
-import { makeStyles } from '@mui/styles';
+import { styled, keyframes } from '@mui/system';
 import { LOADING_STATE } from '@/consts';
 import { LOADING_STATE } from '@/consts';
 import { LoadingType } from '@/components/status/StatusIcon';
 import { LoadingType } from '@/components/status/StatusIcon';
-import type { Theme } from '@mui/material/styles';
 
 
 export type StatusType = {
 export type StatusType = {
   status: LOADING_STATE;
   status: LOADING_STATE;
   percentage?: number;
   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: FC<StatusType> = props => {
   const { status, percentage = 0 } = props;
   const { status, percentage = 0 } = props;
   const { t: commonTrans } = useTranslation();
   const { t: commonTrans } = useTranslation();
   const theme = useTheme();
   const theme = useTheme();
   const statusTrans = commonTrans('status');
   const statusTrans = commonTrans('status');
+
   const { label, color } = useMemo(() => {
   const { label, color } = useMemo(() => {
     switch (status) {
     switch (status) {
       case LOADING_STATE.UNLOADED:
       case LOADING_STATE.UNLOADED:
@@ -69,6 +65,7 @@ const Status: FC<StatusType> = props => {
           label: statusTrans.loaded,
           label: statusTrans.loaded,
           color: '#06f3af',
           color: '#06f3af',
         };
         };
+
       case LOADING_STATE.LOADING:
       case LOADING_STATE.LOADING:
         return {
         return {
           label: `${percentage}% ${statusTrans.loading}`,
           label: `${percentage}% ${statusTrans.loading}`,
@@ -81,23 +78,19 @@ const Status: FC<StatusType> = props => {
           color: '#f25c06',
           color: '#f25c06',
         };
         };
     }
     }
-  }, [status, statusTrans, percentage]);
-
-  const classes = useStyles({ color });
+  }, [status, statusTrans, percentage, theme.palette.primary.main]);
 
 
   return (
   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 && (
       {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 CircularProgress from '@mui/material/CircularProgress';
 import { FC, ReactElement } from 'react';
 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 {
 export enum LoadingType {
   CREATING = 'creating',
   CREATING = 'creating',
@@ -15,32 +14,24 @@ export type StatusIconType = {
   size?: number;
   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 StatusIcon: FC<StatusIconType> = props => {
-  const classes = useStyles();
   const { type, className = '', size = 16 } = props;
   const { type, className = '', size = 16 } = props;
 
 
   const getElement = (type: LoadingType): ReactElement => {
   const getElement = (type: LoadingType): ReactElement => {
     switch (type) {
     switch (type) {
       case 'creating':
       case 'creating':
-        return (
-          <CircularProgress
-            size={size}
-            thickness={4}
-            classes={{ svg: classes.svg }}
-          />
-        );
+        return <StyledCircularProgress size={size} thickness={4} />;
       case 'finish':
       case 'finish':
         return <></>;
         return <></>;
       default:
       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;
 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 { rootContext } from '@/context';
 import CustomButton from '../customButton/CustomButton';
 import CustomButton from '../customButton/CustomButton';
 import { FILE_MIME_TYPE } from '@/consts';
 import { FILE_MIME_TYPE } from '@/consts';
-import { makeStyles } from '@mui/styles';
-import type { Theme } from '@mui/material/styles';
 import type { UploaderProps } from './Types';
 import type { UploaderProps } from './Types';
 
 
-const getStyles = makeStyles((theme: Theme) => ({
-  btn: {},
-}));
-
 const Uploader: FC<UploaderProps> = ({
 const Uploader: FC<UploaderProps> = ({
   label,
   label,
   accept,
   accept,
@@ -25,7 +19,6 @@ const Uploader: FC<UploaderProps> = ({
 }) => {
 }) => {
   const inputRef = useRef(null);
   const inputRef = useRef(null);
   const type = useRef<FILE_MIME_TYPE>(FILE_MIME_TYPE.CSV);
   const type = useRef<FILE_MIME_TYPE>(FILE_MIME_TYPE.CSV);
-  const classes = getStyles();
 
 
   const { openSnackBar } = useContext(rootContext);
   const { openSnackBar } = useContext(rootContext);
 
 
@@ -75,7 +68,7 @@ const Uploader: FC<UploaderProps> = ({
     <section>
     <section>
       <CustomButton
       <CustomButton
         variant="contained"
         variant="contained"
-        className={`${classes.btn} ${btnClass}`}
+        className={`${btnClass}`}
         onClick={handleUpload}
         onClick={handleUpload}
         disabled={disabled}
         disabled={disabled}
         tooltip={disabled ? disableTooltip : ''}
         tooltip={disabled ? disableTooltip : ''}

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

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

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

@@ -12,6 +12,7 @@ const userTrans = {
   newPassword: 'New Password',
   newPassword: 'New Password',
   confirmPassword: 'Confirm Password',
   confirmPassword: 'Confirm Password',
   update: 'Update Password',
   update: 'Update Password',
+  changePassword: 'Change Password',
   isNotSame: 'Not same as new password',
   isNotSame: 'Not same as new password',
   deleteTip:
   deleteTip:
     'Please select at least one item to drop and the root user can not be dropped.',
     '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 { PaletteMode } from '@mui/material';
 import { grey } from '@mui/material/colors';
 import { grey } from '@mui/material/colors';
+import { Theme } from '@mui/material/styles';
+import { ButtonProps } from '@mui/material/Button';
 
 
 declare module '@mui/material/Typography' {
 declare module '@mui/material/Typography' {
   interface TypographyPropsVariantOverrides {
   interface TypographyPropsVariantOverrides {
@@ -126,17 +128,55 @@ export const getAttuTheme = (mode: PaletteMode) => {
       },
       },
       MuiButton: {
       MuiButton: {
         styleOverrides: {
         styleOverrides: {
-          root: {
+          root: ({
+            theme,
+            ownerState,
+          }: {
+            theme: Theme;
+            ownerState: ButtonProps;
+          }) => ({
+            padding: theme.spacing(1, 3),
             textTransform: 'initial',
             textTransform: 'initial',
             fontWeight: 'bold',
             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: {
       MuiDialog: {
         styleOverrides: {
         styleOverrides: {