type(ux): Removed templates tab and minor change in jobCleanUpPolicy (#2886)

* Updated MyHub UI and minor change in jobCleanUpPolicy and removed templates tab
* Removed files related to templates
* Minor styles fix

Signed-off-by: Amit Kumar Das <amit@chaosnative.com>
This commit is contained in:
Amit Kumar Das 2021-06-14 10:01:00 +05:30 committed by GitHub
parent 2913b0fcbd
commit 245f333448
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 108 additions and 694 deletions

View File

@ -1,54 +0,0 @@
export default [
{
workflowID: 0,
title: 'sock-shop-chaos',
chaosinfra: false,
urlToIcon: '/icons/sock-shop.png',
chaosWkfCRDLink: `https://raw.githubusercontent.com/litmuschaos/chaos-charts/${process.env.REACT_APP_HUB_BRANCH_NAME}/workflows/sock-shop-demo/usingCmdProbe/workflow.yaml`,
chaosWkfCRDLink_Recur: `https://raw.githubusercontent.com/litmuschaos/chaos-charts/${process.env.REACT_APP_HUB_BRANCH_NAME}/workflows/sock-shop-demo/usingCmdProbe/workflow_cron.yaml`,
gitLink: `https://github.com/litmuschaos/chaos-charts/${process.env.REACT_APP_HUB_BRANCH_NAME}/workflows/sock-shop-demo`,
experimentPath: 'sock-shop-demo/usingCmdProbe',
provider: 'ChaosNative',
description: 'Induces chaos on Sock-Shop application',
totalRuns: 1000,
isCustom: false,
details:
'This workflow installs and executes chaos on the popular demo application sock-shop, ' +
'that simulates an e-commerce website selling socks. It injects a transient fault on an upstream microservice ' +
'pod (socks catalogue) while continuously checking the availability of the website. This workflow allows execution ' +
'of the same chaos experiment against two versions of the sock-shop deployment: weak and resilient. ' +
'The weak is expected to result in a failed workflow while the resilient succeeds, ' +
'essentially highlighting the need for deployment best-practices.',
recommendation:
'Check whether the application is resilient to the pod failure, once the workflow is completed.',
experimentinfo:
'Provide the application info in spec.appinfo Override the experiment tunables if desired in experiments.spec.components.env',
},
{
workflowID: 1,
title: 'podtato-head-chaos',
chaosinfra: false,
urlToIcon: '/icons/podtato_head.png',
chaosWkfCRDLink: `https://raw.githubusercontent.com/litmuschaos/chaos-charts/${process.env.REACT_APP_HUB_BRANCH_NAME}/workflows/podtato-head/workflow.yaml`,
chaosWkfCRDLink_Recur: `https://raw.githubusercontent.com/litmuschaos/chaos-charts/${process.env.REACT_APP_HUB_BRANCH_NAME}/workflows/podtato-head/workflow_cron.yaml`,
gitLink: `https://github.com/litmuschaos/chaos-charts/tree/${process.env.REACT_APP_HUB_BRANCH_NAME}/workflows/podtato-head`,
experimentPath: 'podtato-head',
provider: 'ChaosNative',
description: 'Induces chaos on podtato-head application',
totalRuns: 300,
isCustom: false,
details:
'This workflow installs and executes chaos on the popular demo application podtato-head, ' +
'A demo project for showcasing cloud-native application delivery use cases using different tools for various use cases.' +
'It injects a transient fault on an upstream microservice ' +
'pod while continuously checking the availability of the website. This workflow allows execution ' +
'of the same chaos experiment against two versions of the podtato-head deployment: weak and resilient. ' +
'The weak is expected to result in a failed workflow while the resilient succeeds, ' +
'essentially highlighting the need for deployment best-practices.',
recommendation:
'Check whether the application is resilient to the pod failure, once the workflow is completed.',
experimentinfo:
'Provide the application info in spec.appinfo Override the experiment tunables if desired ' +
'in experiments.spec.components.env ',
},
];

View File

@ -1,72 +0,0 @@
import React from 'react';
import { preDefinedWorkflowData } from '../../models/predefinedWorkflow';
import useActions from '../../redux/actions';
import * as WorkflowActions from '../../redux/actions/workflow';
import { history } from '../../redux/configureStore';
import { getProjectID, getProjectRole } from '../../utils/getSearchParams';
import CustomCard from '../WorkflowCard';
import CustomWorkflowCard from '../WorkflowCard/CustomWorkflow';
import useStyles from './styles';
interface PredifinedWorkflowsProps {
workflows: preDefinedWorkflowData[];
callbackOnSelectWorkflow: (index: number) => void;
isCustomWorkflowVisible: boolean;
}
const PredifinedWorkflows: React.FC<PredifinedWorkflowsProps> = ({
workflows,
callbackOnSelectWorkflow,
isCustomWorkflowVisible,
}) => {
const workflowAction = useActions(WorkflowActions);
const projectID = getProjectID();
const userRole = getProjectRole();
const classes = useStyles();
return (
<div className={classes.root} data-cy="PredefinedWorkflowsPanel">
{workflows &&
workflows.map((w: preDefinedWorkflowData, index: number) => (
<div key={w.workflowID} data-cy="templatesCard">
<CustomCard
key={w.workflowID}
title={w.title}
urlToIcon={w.urlToIcon}
chaosinfra={w.chaosinfra}
provider={w.provider}
chaosWkfCRDLink={w.chaosWkfCRDLink}
selectedID={w.workflowID}
handleClick={() => callbackOnSelectWorkflow(index)}
description={w.description}
totalRuns={w.totalRuns}
gitLink={w.gitLink}
workflowID={w.workflowID}
/>
</div>
))}
{isCustomWorkflowVisible && (
<div data-cy="CustomWorkflowCard">
<CustomWorkflowCard
handleClick={() => {
workflowAction.setWorkflowDetails({
name: `custom-chaos-workflow-${Math.round(
new Date().getTime() / 1000
)}`,
description: 'Custom Chaos Workflow',
isCustomWorkflow: true,
namespace: 'litmus',
customWorkflows: [],
});
history.push({
pathname: '/create-workflow/custom',
search: `?projectID=${projectID}&projectRole=${userRole}`,
});
}}
/>
</div>
)}
</div>
);
};
export default PredifinedWorkflows;

View File

@ -1,15 +0,0 @@
import { makeStyles } from '@material-ui/core';
const useStyles = makeStyles(() => ({
root: {
display: 'flex',
boxSizing: 'border-box',
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'center',
alignItems: 'center',
gap: 0,
},
}));
export default useStyles;

View File

@ -17,9 +17,6 @@ const CreateWorkflow = lazy(() => import('../../pages/CreateWorkflow'));
const LoginPage = lazy(() => import('../../pages/LoginPage'));
const GetStarted = lazy(() => import('../../pages/GetStartedPage'));
const WorkflowDetails = lazy(() => import('../../pages/WorkflowDetails'));
const BrowseTemplate = lazy(
() => import('../../views/ChaosWorkflows/BrowseTemplate')
);
const HomePage = lazy(() => import('../../pages/HomePage'));
const Community = lazy(() => import('../../pages/Community'));
const Settings = lazy(() => import('../../pages/Settings'));
@ -201,11 +198,6 @@ const Routes: React.FC = () => {
path="/workflows/schedule/:scheduleProjectID/:workflowName/set"
component={SetNewSchedule}
/>
<Route
exact
path="/workflows/template/:templateName"
component={BrowseTemplate}
/>
<Route
exact
path="/workflows/analytics/:workflowRunId"

View File

@ -16,7 +16,6 @@ import { RootState } from '../../redux/reducers';
import { getProjectID, getProjectRole } from '../../utils/getSearchParams';
import BrowseSchedule from '../../views/ChaosWorkflows/BrowseSchedule';
import BrowseWorkflow from '../../views/ChaosWorkflows/BrowseWorkflow';
import Templates from '../../views/ChaosWorkflows/Templates';
import useStyles from './styles';
const Workflows = () => {
@ -80,10 +79,6 @@ const Workflows = () => {
label={`${t('workflows.schedules')}`}
data-cy="browseSchedule"
/>
<StyledTab
label={`${t('workflows.templates')}`}
data-cy="templates"
/>
</Tabs>
</AppBar>
<TabPanel value={workflowTabValue} index={0}>
@ -92,9 +87,6 @@ const Workflows = () => {
<TabPanel value={workflowTabValue} index={1}>
<BrowseSchedule />
</TabPanel>
<TabPanel value={workflowTabValue} index={2}>
<Templates />
</TabPanel>
</Scaffold>
);
};

View File

@ -1,45 +0,0 @@
import { Typography } from '@material-ui/core';
import React from 'react';
import AdjustedWeight from '../../../components/AdjustedWeights';
import useStyles from './styles';
interface ExperimentDetailsProps {
testNames: string[];
testWeights: number[];
experimentinfo?: string | undefined;
}
const ExperimentDetails: React.FC<ExperimentDetailsProps> = ({
testNames,
testWeights,
experimentinfo,
}) => {
const classes = useStyles();
return (
<div>
<Typography className={classes.headerText}>
Experiment details:
</Typography>
<div className={classes.experimentWrapperDiv}>
{testNames &&
testNames.map((test, i) => {
// Mapping all the experiments from a selected workflow
return (
<div className={classes.tests}>
<AdjustedWeight
testName={test}
testValue={testWeights[i]}
spacing
icon
/>
<Typography>{experimentinfo}</Typography>
</div>
);
})}
</div>
</div>
);
};
export default ExperimentDetails;

View File

@ -1,28 +0,0 @@
import { Typography } from '@material-ui/core';
import React from 'react';
import capitalize from '../../../utils/capitalize';
import useStyles from './styles';
interface HeadProps {
image?: string;
title?: string;
details?: string;
}
const Head: React.FC<HeadProps> = ({ image, title, details }) => {
const classes = useStyles();
return (
<div className={classes.flexRow}>
<img src={image} alt="workflowIcon" className={classes.bgIcon} />
<div className={classes.body}>
<Typography data-cy="expName" className={classes.headerText}>
{/* Converting 'some-experiment' to 'Some Experiment' using capitalize utility */}
{title?.split('-').map((text) => `${capitalize(text)} `)}
</Typography>
<Typography className={classes.bodytext}>{details}</Typography>
</div>
</div>
);
};
export default Head;

View File

@ -1,30 +0,0 @@
import React from 'react';
import { Typography } from '@material-ui/core';
import useStyles from './styles';
interface RecommendationProps {
recommendation?: string;
}
const Recommendation: React.FC<RecommendationProps> = ({ recommendation }) => {
const classes = useStyles();
return (
<div className={classes.flexRow}>
<div className={classes.body}>
<Typography className={classes.headerText}>
<strong>Recommendation</strong>
</Typography>
<div className={classes.bodytext}>
<Typography align="left" className={classes.bodytext}>
{recommendation}
</Typography>
</div>
</div>
<img src="/icons/like.svg" alt="Like" className={classes.bgIcon} />
</div>
);
};
export default Recommendation;

View File

@ -1,107 +0,0 @@
import { Divider, Typography } from '@material-ui/core';
import { ButtonFilled, ButtonOutlined } from 'litmus-ui';
import localforage from 'localforage';
import React from 'react';
import { useTranslation } from 'react-i18next';
import BackButton from '../../../components/Button/BackButton';
import data from '../../../components/PredifinedWorkflows/data';
import Scaffold from '../../../containers/layouts/Scaffold';
import { ChooseWorkflowRadio } from '../../../models/localforage/radioButton';
import { preDefinedWorkflowData } from '../../../models/predefinedWorkflow';
import { LocationState } from '../../../models/routerModel';
import useActions from '../../../redux/actions';
import * as WorkflowActions from '../../../redux/actions/workflow';
import { history } from '../../../redux/configureStore';
import { getProjectID, getProjectRole } from '../../../utils/getSearchParams';
import ExperimentDetails from './ExperimentDetails';
import Head from './Head';
import Recommendation from './Recommendation';
import useStyles from './styles';
interface LocationObjectProps {
workflowData: preDefinedWorkflowData;
testNames: string[];
testWeights: number[];
}
interface BrowseTemplateProps {
location: LocationState<LocationObjectProps>;
}
const BrowseAWorkflow: React.FC<BrowseTemplateProps> = ({ location }) => {
const { t } = useTranslation();
const classes = useStyles();
const projectID = getProjectID();
const userRole = getProjectRole();
const workflowAction = useActions(WorkflowActions);
const { workflowData, testNames, testWeights } = location.state;
const preSelectWorkflow = () => {
data.map((w) => {
if (w.title === workflowData.title) {
const selection: ChooseWorkflowRadio = {
selected: 'A',
id: w.workflowID.toString(),
};
localforage.setItem('selectedScheduleOption', selection);
}
return null;
});
workflowAction.setWorkflowDetails({
isCustomWorkflow: false,
});
history.push({
pathname: '/create-workflow',
search: `?projectID=${projectID}&projectRole=${userRole}`,
});
};
return (
<Scaffold>
<div className={classes.back}>
<BackButton />
</div>
<div className={classes.root}>
<Typography className={classes.headerTitle}>
{t('browseTemplate.browseAWorkflow')}
</Typography>
<Typography variant="subtitle1" className={classes.bodytext}>
{t('browseTemplate.seeDetails')}
</Typography>
<section className={classes.contentWrapper}>
{/* Header */}
<Head
image={workflowData.urlToIcon}
title={workflowData.title}
details={workflowData.details}
/>
<Divider className={classes.m2} />
{/* Experiment Details */}
<ExperimentDetails
testNames={testNames}
testWeights={testWeights}
experimentinfo={workflowData.experimentinfo}
/>
<Divider className={classes.m2} />
{/* Recommendation */}
<Recommendation recommendation={workflowData.recommendation} />
<Divider className={classes.m2} />
{/* Buttons */}
<div className={classes.spaceBetween}>
<ButtonOutlined onClick={() => history.goBack()}>
{t('browseTemplate.back')}
</ButtonOutlined>
<ButtonFilled variant="success" onClick={() => preSelectWorkflow()}>
{t('browseTemplate.scheduleThisTemplate')}
</ButtonFilled>
</div>
</section>
</div>
</Scaffold>
);
};
export default BrowseAWorkflow;

View File

@ -1,77 +0,0 @@
import { makeStyles, Theme } from '@material-ui/core/styles';
const useStyles = makeStyles((theme: Theme) => ({
root: {
background: theme.palette.background.paper,
width: '90%',
margin: '0 auto',
padding: theme.spacing(2),
},
back: {
width: '90%',
margin: '0 auto',
padding: theme.spacing(1, 0),
},
contentWrapper: {
background: theme.palette.cards.background,
padding: theme.spacing(1),
},
headerTitle: {
fontSize: '2rem',
},
bodytext: {
marginTop: theme.spacing(3.25),
marginBottom: theme.spacing(3),
fontSize: '1.0625rem',
},
m2: {
margin: '2rem 0',
},
spaceBetween: {
display: 'flex',
justifyContent: 'space-between',
},
// Head
flexRow: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-even',
},
headerText: {
marginTop: theme.spacing(1.25),
fontSize: '1.5625rem',
fontWeight: 800,
},
body: {
width: '70%',
},
bgIcon: {
width: '7rem',
height: '100%',
margin: '1rem 3rem',
},
progress: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
marginLeft: theme.spacing(5),
width: theme.spacing(79.5),
},
// Experiment Details
experimentWrapperDiv: {
display: 'grid',
margin: theme.spacing(2, 0),
gridTemplateColumns: 'repeat(auto-fit, minmax(15rem, 1fr))',
gridGap: '1.5rem',
},
tests: {
width: '17rem',
margin: theme.spacing(0, 2, 2, 0),
},
}));
export default useStyles;

View File

@ -1,72 +0,0 @@
import React from 'react';
import PredifinedWorkflows from '../../../components/PredifinedWorkflows';
import workflowData from '../../../components/PredifinedWorkflows/data';
import useActions from '../../../redux/actions';
import * as TemplateSelectionActions from '../../../redux/actions/template';
import { history } from '../../../redux/configureStore';
import { getProjectID, getProjectRole } from '../../../utils/getSearchParams';
import parsed from '../../../utils/yamlUtils';
import useStyles from './styles';
const Templates = () => {
const classes = useStyles();
const projectID = getProjectID();
const projectRole = getProjectRole();
const template = useActions(TemplateSelectionActions);
const testNames: string[] = [];
const testWeights: number[] = [];
// Setting initial selected template ID to 0
template.selectTemplate({ selectedTemplateID: -1, isDisable: true });
const selectWorkflow = (index: number) => {
// Updating template ID to the selected one
template.selectTemplate({
selectedTemplateID: index,
isDisable: false,
});
fetch(workflowData[index].chaosWkfCRDLink)
.then((data) => {
data.text().then((yamlText) => {
const tests = parsed(yamlText);
tests.forEach((test) => {
// Pushing the individual test names of the selected workflow
testNames.push(test);
// The default weight of the all the experiments in the workflow is 10
testWeights.push(10);
});
history.push({
pathname: `/workflows/template/${workflowData[index].title}`,
search: `?projectID=${projectID}&projectRole=${projectRole}`,
state: {
workflowData: workflowData[index],
testNames,
testWeights,
},
});
});
})
.catch((err) => {
console.error(err);
});
};
return (
<div className={classes.root}>
<div data-cy="templatesPage" className={classes.predefinedCards}>
<PredifinedWorkflows
callbackOnSelectWorkflow={(index: number) => {
selectWorkflow(index);
}}
workflows={workflowData}
isCustomWorkflowVisible={false}
/>
</div>
</div>
);
};
export default Templates;

View File

@ -1,53 +0,0 @@
import { makeStyles } from '@material-ui/core';
const useStyles = makeStyles((theme) => ({
root: {
width: '90%',
margin: '0 auto',
marginTop: theme.spacing(2.5),
marginBottom: theme.spacing(2.5),
},
predefinedCards: {
display: 'inline-block',
},
header: {
padding: '1rem 0.5rem',
display: 'flex',
justifyContent: 'space-between',
},
headerSize: {
fontSize: theme.spacing(2),
},
}));
// CSS For sort div as well as the icon
/*
sort: {
display: 'flex',
cursor: 'pointer',
},
sortIcon: {
width: theme.spacing(4),
marginRight: theme.spacing(1),
position: 'relative',
},
line: {
height: theme.spacing(0.3),
width: '100%',
margin: '0.3em 0',
background: theme.palette.primary.contrastText,
borderRadius: '0.5rem',
},
first: {
width: '90%',
},
second: {
width: '60%',
},
third: {
width: '30%',
},
*/
export default useStyles;

View File

@ -89,7 +89,7 @@ const TargetApplication: React.FC<TargetApplicationProp> = ({
typeof engineManifest.spec.annotationCheck === 'boolean'
? engineManifest.spec.annotationCheck
: engineManifest.spec.annotationCheck === 'true',
jobCleanUpPolicy: engineManifest.spec.jobCleanUpPolicy,
jobCleanUpPolicy: engineManifest.spec.jobCleanUpPolicy ?? 'retain',
});
const [addNodeSelector, setAddNodeSelector] = useState<boolean>(
!!engineManifest.spec.experiments[0].spec.components['nodeSelectors']

View File

@ -1,16 +1,13 @@
import { useQuery } from '@apollo/client';
import {
Accordion,
AccordionDetails,
AccordionSummary,
Backdrop,
Typography,
} from '@material-ui/core';
import { AppBar, Backdrop, Typography } from '@material-ui/core';
import Tabs from '@material-ui/core/Tabs';
import moment from 'moment';
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import useTheme from '@material-ui/core/styles/useTheme';
import { StyledTab, TabPanel } from '../../../components/Tabs';
import Loader from '../../../components/Loader';
import Center from '../../../containers/layouts/Center';
import Scaffold from '../../../containers/layouts/Scaffold';
@ -44,6 +41,8 @@ const MyHub: React.FC = () => {
variables: { data: projectID },
fetchPolicy: 'cache-and-network',
});
const theme = useTheme();
const [tabValue, setTabValue] = useState(1);
// Filter the selected MyHub
const UserHub = hubDetails?.getHubStatus.filter((myHub) => {
@ -99,11 +98,8 @@ const MyHub: React.FC = () => {
return 'Date not available';
};
const [expanded, setExpanded] = React.useState<string | false>('panel2');
const handleChange =
(panel: string) => (event: React.ChangeEvent<{}>, newExpanded: boolean) => {
setExpanded(newExpanded ? panel : false);
const handleTabChange = (event: React.ChangeEvent<{}>, newValue: number) => {
setTabValue(newValue);
};
useEffect(() => {
@ -165,24 +161,28 @@ const MyHub: React.FC = () => {
</Typography>
{/* </div> */}
</div>
<Accordion
square
classes={{
root: classes.MuiAccordionroot,
<AppBar position="static" color="default" className={classes.appBar}>
<Tabs
value={tabValue}
onChange={handleTabChange}
TabIndicatorProps={{
style: {
backgroundColor: theme.palette.highlight,
},
}}
expanded={expanded === 'panel1'}
onChange={handleChange('panel1')}
variant="fullWidth"
>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1d-content"
id="panel1d-header"
>
<Typography variant="h4">
<strong>{t('myhub.myhubChart.preDefined')}</strong>
</Typography>
</AccordionSummary>
<AccordionDetails>
<StyledTab
label={`${t('myhub.myhubChart.preDefined')}`}
data-cy="browseWorkflow"
/>
<StyledTab
label={`${t('myhub.myhubChart.chaosCharts')}`}
data-cy="browseSchedule"
/>
</Tabs>
</AppBar>
<TabPanel value={tabValue} index={0}>
<div className={classes.mainDiv}>
<HeaderSection
searchValue={searchPredefined}
@ -211,8 +211,7 @@ const MyHub: React.FC = () => {
<img
src="/icons/no-experiment-found.svg"
alt="no experiment"
width="80px"
height="80px"
className={classes.noExpImage}
/>
<Typography variant="h5" className={classes.noExp}>
{t('myhub.myhubChart.noPredefinedExp')}
@ -221,27 +220,8 @@ const MyHub: React.FC = () => {
)}
</div>
</div>
</AccordionDetails>
</Accordion>
<Accordion
square
classes={{
root: classes.MuiAccordionroot,
}}
expanded={expanded === 'panel2'}
onChange={handleChange('panel2')}
className={classes.chartAccordion}
>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel2d-content"
id="panel2d-header"
>
<Typography variant="h4">
<strong>{t('myhub.myhubChart.chaosCharts')}</strong>
</Typography>
</AccordionSummary>
<AccordionDetails>
</TabPanel>
<TabPanel value={tabValue} index={1}>
<div className={classes.mainDiv}>
<HeaderSection searchValue={search} changeSearch={changeSearch} />
<div className={classes.chartsGroup}>
@ -264,8 +244,7 @@ const MyHub: React.FC = () => {
<img
src="/icons/no-experiment-found.svg"
alt="no experiment"
width="80px"
height="80px"
className={classes.noExpImage}
/>
<Typography variant="h5" className={classes.noExp}>
{t('myhub.myhubChart.noExp')}
@ -274,8 +253,7 @@ const MyHub: React.FC = () => {
)}
</div>
</div>
</AccordionDetails>
</Accordion>
</TabPanel>
</Scaffold>
);
};

View File

@ -35,8 +35,6 @@ const useStyles = makeStyles((theme) => ({
alignContent: 'center',
alignItems: 'center',
justifyContent: 'center',
border: '0.0625rem solid',
borderColor: theme.palette.border.main,
backgroundColor: theme.palette.common.white,
},
search: {
@ -58,12 +56,10 @@ const useStyles = makeStyles((theme) => ({
display: 'flex',
flexDirection: 'row',
flexWrap: 'wrap',
border: '0.0625rem solid',
marginTop: theme.spacing(3),
paddingTop: theme.spacing(2),
justifyContent: 'center',
alignItems: 'center',
borderColor: theme.palette.border.main,
backgroundColor: theme.palette.common.white,
},
cardDiv: {
@ -112,6 +108,15 @@ const useStyles = makeStyles((theme) => ({
chartAccordion: {
marginTop: theme.spacing(2.5),
},
appBar: {
background: 'transparent',
boxShadow: 'none',
marginBottom: theme.spacing(1),
},
noExpImage: {
width: '5rem',
height: '5rem',
},
}));
export default useStyles;