mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-21 14:50:08 +03:00
* Add new endpoints * Add pool actions * Add form stepper component * Add create application form
This commit is contained in:
parent
22ff32e3d5
commit
4b5b2ad763
@ -50,13 +50,13 @@ module.exports = {
|
||||
type: SUCCESS_ONE_REQUEST,
|
||||
payload: { apps }
|
||||
}),
|
||||
setVNetworks: virtualNetworks => ({
|
||||
setVNetworks: vNetworks => ({
|
||||
type: SUCCESS_ONE_REQUEST,
|
||||
payload: { virtualNetworks }
|
||||
payload: { vNetworks }
|
||||
}),
|
||||
setNetworkTemplates: networkTemplates => ({
|
||||
setVNetworkTemplates: vNetworksTemplates => ({
|
||||
type: SUCCESS_ONE_REQUEST,
|
||||
payload: { networkTemplates }
|
||||
payload: { vNetworksTemplates }
|
||||
}),
|
||||
setSecGroups: securityGroups => ({
|
||||
type: SUCCESS_ONE_REQUEST,
|
||||
|
@ -16,6 +16,10 @@ export const toolbar = {
|
||||
sm: 64
|
||||
};
|
||||
|
||||
export const footer = {
|
||||
regular: 30
|
||||
};
|
||||
|
||||
export const sidebar = {
|
||||
minified: 60,
|
||||
fixed: 240
|
||||
|
81
src/fireedge/src/public/components/Cards/NetworkCard.js
Normal file
81
src/fireedge/src/public/components/Cards/NetworkCard.js
Normal file
@ -0,0 +1,81 @@
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
makeStyles,
|
||||
Card,
|
||||
Button,
|
||||
CardHeader,
|
||||
CardActions,
|
||||
Fade
|
||||
} from '@material-ui/core';
|
||||
|
||||
import { Tr } from 'client/components/HOC';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
height: '100%',
|
||||
minHeight: 140,
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
},
|
||||
header: {
|
||||
overflowX: 'hidden',
|
||||
flexGrow: 1
|
||||
},
|
||||
subheader: {
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'initial',
|
||||
display: '-webkit-box',
|
||||
lineClamp: 2,
|
||||
boxOrient: 'vertical'
|
||||
},
|
||||
remove: {
|
||||
backgroundColor: theme.palette.error.dark
|
||||
}
|
||||
}));
|
||||
|
||||
const NetworkCard = React.memo(
|
||||
({ info, handleEdit, handleClone, handleRemove }) => {
|
||||
const classes = useStyles();
|
||||
const { mandatory, name, description, type, id, extra } = info;
|
||||
|
||||
return (
|
||||
<Fade in unmountOnExit={false}>
|
||||
<Card className={classes.root}>
|
||||
<CardHeader
|
||||
avatar={mandatory ? 'M' : ''}
|
||||
className={classes.header}
|
||||
classes={{ content: classes.headerContent }}
|
||||
title={name}
|
||||
titleTypographyProps={{
|
||||
variant: 'body2',
|
||||
noWrap: true,
|
||||
title: name
|
||||
}}
|
||||
subheader={description}
|
||||
subheaderTypographyProps={{
|
||||
variant: 'body2',
|
||||
noWrap: true,
|
||||
className: classes.subheader,
|
||||
title: description
|
||||
}}
|
||||
/>
|
||||
<CardActions>
|
||||
<Button variant="contained" size="small" onClick={handleEdit}>
|
||||
{Tr('Edit')}
|
||||
</Button>
|
||||
<Button variant="contained" size="small" onClick={handleClone}>
|
||||
{Tr('Clone')}
|
||||
</Button>
|
||||
<Button size="small" onClick={handleRemove}>
|
||||
{Tr('Remove')}
|
||||
</Button>
|
||||
</CardActions>
|
||||
</Card>
|
||||
</Fade>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default NetworkCard;
|
105
src/fireedge/src/public/components/Cards/RoleCard.js
Normal file
105
src/fireedge/src/public/components/Cards/RoleCard.js
Normal file
@ -0,0 +1,105 @@
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
makeStyles,
|
||||
Card,
|
||||
Button,
|
||||
CardHeader,
|
||||
CardActions,
|
||||
Badge,
|
||||
Fade
|
||||
} from '@material-ui/core';
|
||||
import DesktopWindowsIcon from '@material-ui/icons/DesktopWindows';
|
||||
|
||||
import { Tr } from 'client/components/HOC';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
height: '100%',
|
||||
minHeight: 140,
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
},
|
||||
header: {
|
||||
overflowX: 'hidden',
|
||||
flexGrow: 1
|
||||
},
|
||||
headerContent: {},
|
||||
title: {
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'initial',
|
||||
display: '-webkit-box',
|
||||
lineClamp: 2,
|
||||
boxOrient: 'vertical'
|
||||
}
|
||||
}));
|
||||
|
||||
const RoleCard = React.memo(
|
||||
({ info, handleEdit, handleClone, handleRemove }) => {
|
||||
const classes = useStyles();
|
||||
const {
|
||||
name = 'Role name',
|
||||
cardinality,
|
||||
vm_template = 0,
|
||||
elasticity_policies,
|
||||
scheduled_policies
|
||||
} = info;
|
||||
|
||||
const ConditionalWrapper = ({ condition, wrapper, children }) =>
|
||||
condition ? wrapper(children) : children;
|
||||
console.log(info);
|
||||
return (
|
||||
<Fade in unmountOnExit={false}>
|
||||
<Card className={classes.root}>
|
||||
<CardHeader
|
||||
avatar={
|
||||
<ConditionalWrapper
|
||||
condition={cardinality > 1}
|
||||
wrapper={children => (
|
||||
<Badge
|
||||
badgeContent={cardinality}
|
||||
color="primary"
|
||||
anchorOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'left'
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Badge>
|
||||
)}
|
||||
>
|
||||
<DesktopWindowsIcon />
|
||||
</ConditionalWrapper>
|
||||
}
|
||||
className={classes.header}
|
||||
classes={{ content: classes.headerContent }}
|
||||
title={name}
|
||||
titleTypographyProps={{
|
||||
variant: 'body2',
|
||||
noWrap: true,
|
||||
className: classes.title,
|
||||
title: name
|
||||
}}
|
||||
subheader={`Template id: ${vm_template}`}
|
||||
subheaderTypographyProps={{
|
||||
variant: 'body2',
|
||||
noWrap: true,
|
||||
title: `Template id: ${vm_template}`
|
||||
}}
|
||||
/>
|
||||
<CardActions>
|
||||
<Button variant="contained" size="small" onClick={handleEdit}>
|
||||
{Tr('Edit')}
|
||||
</Button>
|
||||
<Button size="small" onClick={handleRemove}>
|
||||
{Tr('Remove')}
|
||||
</Button>
|
||||
</CardActions>
|
||||
</Card>
|
||||
</Fade>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default RoleCard;
|
4
src/fireedge/src/public/components/Cards/index.js
Normal file
4
src/fireedge/src/public/components/Cards/index.js
Normal file
@ -0,0 +1,4 @@
|
||||
import NetworkCard from './NetworkCard';
|
||||
import RoleCard from './RoleCard';
|
||||
|
||||
export { NetworkCard, RoleCard };
|
234
src/fireedge/src/public/components/Dialogs/NetworkDialog.js
Normal file
234
src/fireedge/src/public/components/Dialogs/NetworkDialog.js
Normal file
@ -0,0 +1,234 @@
|
||||
import React, { useMemo, useEffect } from 'react';
|
||||
|
||||
import {
|
||||
makeStyles,
|
||||
useMediaQuery,
|
||||
Button,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Grid,
|
||||
FormControlLabel,
|
||||
Checkbox,
|
||||
TextField,
|
||||
MenuItem
|
||||
} from '@material-ui/core';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import { yupResolver } from '@hookform/resolvers';
|
||||
import * as yup from 'yup';
|
||||
|
||||
import useOpennebula from 'client/hooks/useOpennebula';
|
||||
import ErrorHelper from 'client/components/FormControl/ErrorHelper';
|
||||
import { Tr } from 'client/components/HOC';
|
||||
|
||||
const useStyles = makeStyles(theme => ({}));
|
||||
|
||||
const SELECT = {
|
||||
template: 'template',
|
||||
network: 'network'
|
||||
};
|
||||
|
||||
const TYPES_NETWORKS = {
|
||||
template_id: { text: 'Create', select: SELECT.template, extra: true },
|
||||
reserve_from: { text: 'Reserve', select: SELECT.network, extra: true },
|
||||
id: { text: 'Existing', select: SELECT.network, extra: false }
|
||||
};
|
||||
|
||||
const ID_CY = 'form-network';
|
||||
|
||||
const NetworkDialog = React.memo(
|
||||
({ open, info: network, onSubmit, onCancel }) => {
|
||||
const classes = useStyles();
|
||||
const { vNetworks, vNetworksTemplates } = useOpennebula();
|
||||
const isMobile = useMediaQuery(theme => theme.breakpoints.only('xs'));
|
||||
|
||||
const { register, handleSubmit, errors, control, watch } = useForm({
|
||||
reValidateMode: 'onSubmit',
|
||||
defaultValues: {
|
||||
type: Object.keys(TYPES_NETWORKS)[0],
|
||||
...network
|
||||
},
|
||||
resolver: yupResolver(
|
||||
yup.object().shape({
|
||||
mandatory: yup.boolean().required(),
|
||||
name: yup.string().required('Name is a required field'),
|
||||
description: yup.string(),
|
||||
type: yup
|
||||
.string()
|
||||
.oneOf(Object.keys(TYPES_NETWORKS))
|
||||
.required('Type is required field'),
|
||||
id: yup
|
||||
.string()
|
||||
.when('type', {
|
||||
is: type =>
|
||||
Object.entries(TYPES_NETWORKS)?.some(
|
||||
([key, { select }]) =>
|
||||
type === key && select === SELECT.network
|
||||
),
|
||||
then: yup.string().required('Network is required field'),
|
||||
otherwise: yup
|
||||
.string()
|
||||
.required('Network template is required field')
|
||||
})
|
||||
.required(),
|
||||
extra: yup.string()
|
||||
})
|
||||
)
|
||||
});
|
||||
|
||||
const { type } = watch();
|
||||
const typeSelected = TYPES_NETWORKS[type]?.select;
|
||||
|
||||
const selectType =
|
||||
typeSelected === SELECT.network ? vNetworks : vNetworksTemplates;
|
||||
|
||||
return (
|
||||
<Dialog fullScreen={isMobile} open={open} maxWidth="lg" scroll="paper">
|
||||
<DialogTitle id={`${ID_CY}-title`}>
|
||||
{network?.name ? 'Edit network' : 'New network'}
|
||||
</DialogTitle>
|
||||
<DialogContent dividers>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
name="mandatory"
|
||||
color="primary"
|
||||
defaultChecked={network?.mandatory}
|
||||
inputProps={{ 'data-cy': `${ID_CY}-mandatory` }}
|
||||
inputRef={register}
|
||||
/>
|
||||
}
|
||||
label={Tr('Mandatory')}
|
||||
labelPlacement="end"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
fullWidth
|
||||
name="name"
|
||||
label={Tr('Name')}
|
||||
inputRef={register}
|
||||
inputProps={{ 'data-cy': `${ID_CY}-name` }}
|
||||
error={errors.name}
|
||||
helperText={
|
||||
errors.name && <ErrorHelper label={errors.name?.message} />
|
||||
}
|
||||
FormHelperTextProps={{ 'data-cy': `${ID_CY}-name-error` }}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
fullWidth
|
||||
multiline
|
||||
name="description"
|
||||
label={Tr('Description')}
|
||||
inputRef={register}
|
||||
inputProps={{ 'data-cy': `${ID_CY}-description` }}
|
||||
error={errors.description}
|
||||
helperText={
|
||||
errors.description && (
|
||||
<ErrorHelper label={errors.description?.message} />
|
||||
)
|
||||
}
|
||||
FormHelperTextProps={{
|
||||
'data-cy': `${ID_CY}-description-error`
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<Controller
|
||||
as={
|
||||
<TextField
|
||||
select
|
||||
fullWidth
|
||||
inputProps={{ 'data-cy': `${ID_CY}-type` }}
|
||||
label={Tr('Select a type')}
|
||||
error={errors.type}
|
||||
helperText={
|
||||
errors.type && (
|
||||
<ErrorHelper label={errors.type?.message} />
|
||||
)
|
||||
}
|
||||
FormHelperTextProps={{
|
||||
'data-cy': `${ID_CY}-type-error`
|
||||
}}
|
||||
>
|
||||
{Object.entries(TYPES_NETWORKS).map(([key, { text }]) => (
|
||||
<MenuItem key={`type-${key}`} value={key}>
|
||||
{text}
|
||||
</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
}
|
||||
name="type"
|
||||
control={control}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<Controller
|
||||
as={
|
||||
<TextField
|
||||
select
|
||||
fullWidth
|
||||
inputProps={{ 'data-cy': `${ID_CY}-id` }}
|
||||
label={Tr('Select a') + SELECT[typeSelected]}
|
||||
error={errors.id}
|
||||
helperText={
|
||||
errors.id && <ErrorHelper label={errors.id?.message} />
|
||||
}
|
||||
FormHelperTextProps={{
|
||||
'data-cy': `${ID_CY}-id-error`
|
||||
}}
|
||||
>
|
||||
{selectType?.map(({ ID, NAME }) => (
|
||||
<MenuItem key={`${typeSelected}-${ID}`} value={ID}>
|
||||
{NAME}
|
||||
</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
}
|
||||
name="id"
|
||||
control={control}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
fullWidth
|
||||
multiline
|
||||
name="extra"
|
||||
label={Tr('Extra template')}
|
||||
inputRef={register}
|
||||
inputProps={{ 'data-cy': `${ID_CY}-extra` }}
|
||||
error={errors.extra}
|
||||
helperText={
|
||||
errors.extra && <ErrorHelper label={errors.extra?.message} />
|
||||
}
|
||||
FormHelperTextProps={{
|
||||
'data-cy': `${ID_CY}-extra-error`
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={onCancel} color="primary">
|
||||
{Tr('Cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={handleSubmit(onSubmit)}
|
||||
>
|
||||
{Tr('Save')}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default NetworkDialog;
|
96
src/fireedge/src/public/components/Dialogs/RoleDialog.js
Normal file
96
src/fireedge/src/public/components/Dialogs/RoleDialog.js
Normal file
@ -0,0 +1,96 @@
|
||||
import React, { useMemo, useEffect } from 'react';
|
||||
|
||||
import {
|
||||
makeStyles,
|
||||
useMediaQuery,
|
||||
Button,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Grid,
|
||||
FormControlLabel,
|
||||
Checkbox,
|
||||
TextField,
|
||||
MenuItem
|
||||
} from '@material-ui/core';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import { yupResolver } from '@hookform/resolvers';
|
||||
import * as yup from 'yup';
|
||||
|
||||
import useOpennebula from 'client/hooks/useOpennebula';
|
||||
import ErrorHelper from 'client/components/FormControl/ErrorHelper';
|
||||
import { Tr } from 'client/components/HOC';
|
||||
|
||||
const useStyles = makeStyles(theme => ({}));
|
||||
|
||||
const ID_CY = 'form-role';
|
||||
|
||||
const NetworkDialog = React.memo(({ open, info: role, onSubmit, onCancel }) => {
|
||||
// const classes = useStyles();
|
||||
const { templates } = useOpennebula();
|
||||
const isMobile = useMediaQuery(theme => theme.breakpoints.only('xs'));
|
||||
|
||||
const { register, handleSubmit, errors, control } = useForm({
|
||||
reValidateMode: 'onSubmit',
|
||||
defaultValues: role
|
||||
// resolver: yupResolver(yup.object().shape({}))
|
||||
});
|
||||
|
||||
return (
|
||||
<Dialog fullScreen={isMobile} open={open} maxWidth="lg" scroll="paper">
|
||||
<DialogTitle id={`${ID_CY}-title`}>
|
||||
{role?.name ? 'Edit role' : 'New role'}
|
||||
</DialogTitle>
|
||||
<DialogContent dividers>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12}>
|
||||
{'ROLE FORM'}
|
||||
{/* <Controller
|
||||
as={
|
||||
<TextField
|
||||
select
|
||||
fullWidth
|
||||
inputProps={{ 'data-cy': `${ID_CY}-id` }}
|
||||
label={Tr('Select a vm')}
|
||||
error={errors.template}
|
||||
helperText={
|
||||
errors.template && (
|
||||
<ErrorHelper label={errors.template?.message} />
|
||||
)
|
||||
}
|
||||
FormHelperTextProps={{
|
||||
'data-cy': `${ID_CY}-id-error`
|
||||
}}
|
||||
>
|
||||
{templates?.map(({ ID, NAME }) => (
|
||||
<MenuItem key={`template-${ID}`} value={ID}>
|
||||
{NAME}
|
||||
</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
}
|
||||
name="template"
|
||||
control={control}
|
||||
/> */}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={onCancel} color="primary">
|
||||
{Tr('Cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={handleSubmit(() => onSubmit(role))}
|
||||
>
|
||||
{Tr('Save')}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
});
|
||||
|
||||
export default NetworkDialog;
|
4
src/fireedge/src/public/components/Dialogs/index.js
Normal file
4
src/fireedge/src/public/components/Dialogs/index.js
Normal file
@ -0,0 +1,4 @@
|
||||
import NetworkDialog from './NetworkDialog';
|
||||
import RoleDialog from './RoleDialog';
|
||||
|
||||
export { NetworkDialog, RoleDialog };
|
@ -14,7 +14,7 @@
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
import React from 'react';
|
||||
import { Box, Link } from '@material-ui/core';
|
||||
import { Box, Link, Typography } from '@material-ui/core';
|
||||
|
||||
import footerStyles from 'client/components/Footer/styles';
|
||||
import { by } from 'client/constants';
|
||||
@ -26,14 +26,16 @@ const Footer = React.memo(() => {
|
||||
|
||||
return (
|
||||
<Box className={classes.footer} component="footer">
|
||||
{'Made with'}
|
||||
<span className={classes.heartIcon} role="img" aria-label="heart-emoji">
|
||||
{'❤️'}
|
||||
</span>
|
||||
{'by'}
|
||||
<Link href={url} className={classes.link}>
|
||||
{text}
|
||||
</Link>
|
||||
<Typography variant="body2">
|
||||
{'Made with'}
|
||||
<span className={classes.heartIcon} role="img" aria-label="heart-emoji">
|
||||
{'❤️'}
|
||||
</span>
|
||||
{'by'}
|
||||
<Link href={url} className={classes.link}>
|
||||
{text}
|
||||
</Link>
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
|
@ -42,7 +42,14 @@ const GroupSelect = props => {
|
||||
?.map(({ ID, NAME }) => (
|
||||
<MenuItem key={`selector-group-${ID}`} value={String(ID)}>
|
||||
{`${ID} - ${String(NAME)}`}
|
||||
{authUser?.GID === ID && <FilterVintage fontSize="small" />}
|
||||
{authUser?.GID === ID && (
|
||||
<FilterVintage
|
||||
style={{
|
||||
fontSize: '1rem',
|
||||
marginLeft: 16
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</MenuItem>
|
||||
)),
|
||||
[groups]
|
||||
|
124
src/fireedge/src/public/components/FormStepper/FormStep.js
Normal file
124
src/fireedge/src/public/components/FormStepper/FormStep.js
Normal file
@ -0,0 +1,124 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import {
|
||||
makeStyles,
|
||||
Box,
|
||||
CardActionArea,
|
||||
CardContent,
|
||||
Card,
|
||||
Grid
|
||||
} from '@material-ui/core';
|
||||
import { Add } from '@material-ui/icons';
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
cardPlus: {
|
||||
height: '100%',
|
||||
minHeight: 140,
|
||||
display: 'flex',
|
||||
textAlign: 'center'
|
||||
}
|
||||
}));
|
||||
|
||||
function FormStep({ step, data, setFormData }) {
|
||||
const classes = useStyles();
|
||||
const [dialogFormData, setDialogFormData] = useState({});
|
||||
const [showDialog, setShowDialog] = useState(false);
|
||||
const { reset } = useFormContext();
|
||||
|
||||
const { id, addAction, InfoComponent, DialogComponent, DEFAULT_DATA } = step;
|
||||
const { [id]: stepData } = data;
|
||||
|
||||
useEffect(() => {
|
||||
reset({ ...data }, { errors: true });
|
||||
}, [id, data]);
|
||||
|
||||
const handleSubmit = values => {
|
||||
setFormData(prevData => ({
|
||||
...prevData,
|
||||
[id]: Object.assign(prevData[id], {
|
||||
[dialogFormData.index]: values
|
||||
})
|
||||
}));
|
||||
|
||||
setShowDialog(false);
|
||||
};
|
||||
|
||||
const handleOpen = (index = stepData?.length) => {
|
||||
const openData = stepData[index] ?? DEFAULT_DATA;
|
||||
|
||||
setDialogFormData({ index, data: openData });
|
||||
setShowDialog(true);
|
||||
};
|
||||
|
||||
const handleClone = index => {
|
||||
const cloneData = { ...stepData[index], name: 'clone' };
|
||||
|
||||
setFormData(prevData => {
|
||||
prevData[id].splice(index + 1, 0, cloneData);
|
||||
return prevData;
|
||||
});
|
||||
};
|
||||
|
||||
const handleRemove = indexRemove => {
|
||||
// TODO confirmation??
|
||||
setFormData(prevData => ({
|
||||
...prevData,
|
||||
[id]: prevData[id]?.filter((_, index) => index !== indexRemove)
|
||||
}));
|
||||
};
|
||||
|
||||
const handleClose = () => setShowDialog(false);
|
||||
|
||||
return (
|
||||
<Box component="form">
|
||||
<Grid container spacing={3}>
|
||||
{addAction && (
|
||||
<Grid item xs={12} sm={4} md={3} lg={2}>
|
||||
<Card className={classes.cardPlus} raised>
|
||||
<CardActionArea onClick={() => handleOpen()}>
|
||||
<CardContent>
|
||||
<Add />
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
</Card>
|
||||
</Grid>
|
||||
)}
|
||||
{Array.isArray(stepData) &&
|
||||
stepData?.map((info, index) => (
|
||||
<Grid key={`${id}-${index}`} item xs={12} sm={4} md={3} lg={2}>
|
||||
<InfoComponent
|
||||
info={info}
|
||||
handleEdit={() => handleOpen(index)}
|
||||
handleClone={() => handleClone(index)}
|
||||
handleRemove={() => handleRemove(index)}
|
||||
/>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
{showDialog && (
|
||||
<DialogComponent
|
||||
open={showDialog}
|
||||
info={dialogFormData?.data}
|
||||
onSubmit={handleSubmit}
|
||||
onCancel={handleClose}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
FormStep.propTypes = {
|
||||
step: PropTypes.objectOf(PropTypes.object).isRequired,
|
||||
data: PropTypes.objectOf(PropTypes.object).isRequired,
|
||||
setFormData: PropTypes.func
|
||||
};
|
||||
|
||||
FormStep.defaultProps = {
|
||||
step: {},
|
||||
data: {},
|
||||
setFormData: data => data
|
||||
};
|
||||
|
||||
export default FormStep;
|
@ -0,0 +1,55 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { Button, MobileStepper } from '@material-ui/core';
|
||||
import { KeyboardArrowLeft, KeyboardArrowRight } from '@material-ui/icons';
|
||||
|
||||
import { Tr } from 'client/components/HOC';
|
||||
|
||||
const CustomMobileStepper = ({
|
||||
totalSteps,
|
||||
activeStep,
|
||||
lastStep,
|
||||
disabledBack,
|
||||
handleNext,
|
||||
handleBack
|
||||
}) => (
|
||||
<MobileStepper
|
||||
variant="progress"
|
||||
position="static"
|
||||
steps={totalSteps}
|
||||
activeStep={activeStep}
|
||||
style={{ flexGrow: 1 }}
|
||||
backButton={
|
||||
<Button size="small" onClick={handleBack} disabled={disabledBack}>
|
||||
<KeyboardArrowLeft /> {Tr('Back')}
|
||||
</Button>
|
||||
}
|
||||
nextButton={
|
||||
<Button size="small" onClick={handleNext}>
|
||||
{Tr(activeStep === lastStep ? 'Finish' : 'Next')}
|
||||
<KeyboardArrowRight />
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
CustomMobileStepper.propTypes = {
|
||||
totalSteps: PropTypes.number,
|
||||
activeStep: PropTypes.number,
|
||||
lastStep: PropTypes.number,
|
||||
disabledBack: PropTypes.bool,
|
||||
handleNext: PropTypes.func,
|
||||
handleBack: PropTypes.func
|
||||
};
|
||||
|
||||
CustomMobileStepper.defaultProps = {
|
||||
totalSteps: 0,
|
||||
activeStep: 0,
|
||||
lastStep: 0,
|
||||
disabledBack: false,
|
||||
handleNext: () => undefined,
|
||||
handleBack: () => undefined
|
||||
};
|
||||
|
||||
export default CustomMobileStepper;
|
66
src/fireedge/src/public/components/FormStepper/Stepper.js
Normal file
66
src/fireedge/src/public/components/FormStepper/Stepper.js
Normal file
@ -0,0 +1,66 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { Button, Stepper, Step, StepLabel, Box } from '@material-ui/core';
|
||||
|
||||
import { Tr } from 'client/components/HOC';
|
||||
|
||||
/*
|
||||
position: sticky;
|
||||
top: 0;
|
||||
backdrop-filter: blur(5px);
|
||||
background: #000000aa;
|
||||
z-index: 1;
|
||||
*/
|
||||
|
||||
const CustomStepper = ({
|
||||
steps,
|
||||
activeStep,
|
||||
lastStep,
|
||||
disabledBack,
|
||||
handleNext,
|
||||
handleBack
|
||||
}) => (
|
||||
<>
|
||||
<Stepper activeStep={activeStep}>
|
||||
{steps?.map(({ label }) => (
|
||||
<Step key={label}>
|
||||
<StepLabel>{label}</StepLabel>
|
||||
</Step>
|
||||
))}
|
||||
</Stepper>
|
||||
<Box marginY={2}>
|
||||
<Button onClick={handleBack} disabled={disabledBack}>
|
||||
{Tr('Back')}
|
||||
</Button>
|
||||
<Button variant="contained" color="primary" onClick={handleNext}>
|
||||
{Tr(activeStep === lastStep ? 'Finish' : 'Next')}
|
||||
</Button>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
|
||||
CustomStepper.propTypes = {
|
||||
steps: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
id: PropTypes.oneOf([PropTypes.string, PropTypes.number]).isRequired,
|
||||
label: PropTypes.string.isRequired
|
||||
})
|
||||
),
|
||||
activeStep: PropTypes.number.isRequired,
|
||||
lastStep: PropTypes.number.isRequired,
|
||||
disabledBack: PropTypes.bool.isRequired,
|
||||
handleNext: PropTypes.func,
|
||||
handleBack: PropTypes.func
|
||||
};
|
||||
|
||||
CustomStepper.defaultProps = {
|
||||
steps: [],
|
||||
activeStep: 0,
|
||||
lastStep: 0,
|
||||
disabledBack: false,
|
||||
handleNext: () => undefined,
|
||||
handleBack: () => undefined
|
||||
};
|
||||
|
||||
export default CustomStepper;
|
96
src/fireedge/src/public/components/FormStepper/index.js
Normal file
96
src/fireedge/src/public/components/FormStepper/index.js
Normal file
@ -0,0 +1,96 @@
|
||||
import React, { useState, useMemo, useCallback } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { useMediaQuery } from '@material-ui/core';
|
||||
|
||||
import CustomMobileStepper from 'client/components/FormStepper/MobileStepper';
|
||||
import CustomStepper from 'client/components/FormStepper/Stepper';
|
||||
import { console } from 'window-or-global';
|
||||
|
||||
const FIRST_STEP = 0;
|
||||
|
||||
const FormStepper = ({ steps, initialValue, onSubmit }) => {
|
||||
const isMobile = useMediaQuery(theme => theme.breakpoints.only('xs'));
|
||||
const { watch } = useFormContext();
|
||||
const [activeStep, setActiveStep] = useState(FIRST_STEP);
|
||||
const [formData, setFormData] = useState(initialValue);
|
||||
|
||||
const totalSteps = useMemo(() => steps?.length, [steps]);
|
||||
const lastStep = useMemo(() => totalSteps - 1, [totalSteps]);
|
||||
const disabledBack = useMemo(() => activeStep === FIRST_STEP, [activeStep]);
|
||||
|
||||
const handleNext = useCallback(() => {
|
||||
setFormData(data => ({ ...data, ...watch() }));
|
||||
|
||||
if (activeStep === lastStep) {
|
||||
onSubmit(formData);
|
||||
} else setActiveStep(prevActiveStep => prevActiveStep + 1);
|
||||
}, [activeStep]);
|
||||
|
||||
const handleBack = useCallback(() => {
|
||||
if (activeStep <= FIRST_STEP) return;
|
||||
|
||||
setActiveStep(prevActiveStep => prevActiveStep - 1);
|
||||
setFormData(data => ({ ...data, ...watch() }));
|
||||
}, [activeStep]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* STEPPER */}
|
||||
{isMobile ? (
|
||||
<CustomMobileStepper
|
||||
totalSteps={totalSteps}
|
||||
activeStep={activeStep}
|
||||
lastStep={lastStep}
|
||||
disabledBack={disabledBack}
|
||||
handleNext={handleNext}
|
||||
handleBack={handleBack}
|
||||
/>
|
||||
) : (
|
||||
<CustomStepper
|
||||
steps={steps}
|
||||
activeStep={activeStep}
|
||||
lastStep={lastStep}
|
||||
disabledBack={disabledBack}
|
||||
handleNext={handleNext}
|
||||
handleBack={handleBack}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* FORM CONTENT */}
|
||||
{React.useMemo(() => {
|
||||
const { content: Content, ...rest } = steps[activeStep];
|
||||
|
||||
return (
|
||||
<Content
|
||||
data={formData}
|
||||
setFormData={setFormData}
|
||||
step={{ ...rest }}
|
||||
/>
|
||||
);
|
||||
}, [activeStep, formData, setFormData])}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
FormStepper.propTypes = {
|
||||
steps: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
id: PropTypes.number.isRequired,
|
||||
label: PropTypes.string.isRequired,
|
||||
content: PropTypes.node.isRequired,
|
||||
dependOf: PropTypes.bool
|
||||
})
|
||||
),
|
||||
initialValue: PropTypes.objectOf(PropTypes.object),
|
||||
onSubmit: PropTypes.func
|
||||
};
|
||||
|
||||
FormStepper.defaultProps = {
|
||||
steps: [],
|
||||
initialValue: {},
|
||||
onSubmit: dataForm => console.log(dataForm)
|
||||
};
|
||||
|
||||
export default FormStepper;
|
@ -1,5 +1,5 @@
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
import { sidebar, toolbar } from 'client/assets/theme/defaults';
|
||||
import { sidebar, toolbar, footer } from 'client/assets/theme/defaults';
|
||||
|
||||
export default makeStyles(theme => ({
|
||||
root: {
|
||||
@ -23,10 +23,9 @@ export default makeStyles(theme => ({
|
||||
}
|
||||
},
|
||||
main: {
|
||||
paddingBottom: 30,
|
||||
height: '100vh',
|
||||
width: '100%',
|
||||
// paddingTop: 64
|
||||
paddingBottom: footer.regular,
|
||||
paddingTop: toolbar.regular,
|
||||
[`${theme.breakpoints.up('xs')} and (orientation: landscape)`]: {
|
||||
paddingTop: toolbar.xs
|
||||
|
@ -0,0 +1,48 @@
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
import { useForm, FormProvider } from 'react-hook-form';
|
||||
|
||||
import FormStepper from 'client/components/FormStepper';
|
||||
import Steps from 'client/containers/Application/Create/steps';
|
||||
import useOpennebula from 'client/hooks/useOpennebula';
|
||||
|
||||
const INITIAL_VALUE = {
|
||||
networks: [],
|
||||
roles: []
|
||||
};
|
||||
|
||||
function ApplicationCreate() {
|
||||
const { getVNetworks, getVNetworksTemplates, getTemplates } = useOpennebula();
|
||||
const methods = useForm({
|
||||
mode: 'onBlur'
|
||||
});
|
||||
const { watch, errors } = methods;
|
||||
|
||||
useEffect(() => {
|
||||
getVNetworks();
|
||||
getVNetworksTemplates();
|
||||
getTemplates();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// console.log('FORM CONTEXT', watch(), errors);
|
||||
}, [watch, errors]);
|
||||
|
||||
const onSubmit = formData => console.log('submit', formData);
|
||||
|
||||
return (
|
||||
<FormProvider {...methods}>
|
||||
<FormStepper
|
||||
steps={Steps}
|
||||
initialValue={INITIAL_VALUE}
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
</FormProvider>
|
||||
);
|
||||
}
|
||||
|
||||
ApplicationCreate.propTypes = {};
|
||||
|
||||
ApplicationCreate.defaultProps = {};
|
||||
|
||||
export default ApplicationCreate;
|
@ -0,0 +1,45 @@
|
||||
import { NetworkDialog, RoleDialog } from 'client/components/Dialogs';
|
||||
import { NetworkCard, RoleCard } from 'client/components/Cards';
|
||||
import FormStep from 'client/components/FormStepper/FormStep';
|
||||
|
||||
export default [
|
||||
{
|
||||
id: 'networks',
|
||||
label: 'Networks configuration',
|
||||
content: FormStep,
|
||||
preRender: () => undefined,
|
||||
addAction: true,
|
||||
DEFAULT_DATA: {
|
||||
mandatory: true,
|
||||
name: 'Public_dev',
|
||||
description: 'Public network in development mode',
|
||||
type: 'id',
|
||||
id: '0',
|
||||
extra: 'size=5'
|
||||
},
|
||||
InfoComponent: NetworkCard,
|
||||
DialogComponent: NetworkDialog
|
||||
},
|
||||
{
|
||||
id: 'roles',
|
||||
label: 'Defining each role',
|
||||
content: FormStep,
|
||||
preRender: () => undefined,
|
||||
addAction: true,
|
||||
DEFAULT_DATA: {
|
||||
name: 'Master_dev',
|
||||
cardinality: 2,
|
||||
vm_template: 0,
|
||||
elasticity_policies: [],
|
||||
scheduled_policies: []
|
||||
},
|
||||
InfoComponent: RoleCard,
|
||||
DialogComponent: RoleDialog
|
||||
},
|
||||
{
|
||||
id: 'where',
|
||||
label: 'Where will it run?',
|
||||
content: FormStep,
|
||||
preRender: () => undefined
|
||||
}
|
||||
];
|
@ -0,0 +1,3 @@
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
|
||||
export default makeStyles(theme => ({}));
|
@ -38,7 +38,7 @@ const useStyles = makeStyles({
|
||||
}
|
||||
});
|
||||
|
||||
function Users() {
|
||||
function ApplicationDeploy() {
|
||||
const classes = useStyles();
|
||||
const { isLoading } = useGeneral();
|
||||
const { users, groups, getUsers } = useOpennebula();
|
||||
@ -49,22 +49,22 @@ function Users() {
|
||||
}
|
||||
}, [getUsers]);
|
||||
|
||||
const getGroupById = id => groups?.find(({ ID }) => ID === id);
|
||||
const getGroupById = findId => groups?.find(({ ID }) => ID === findId);
|
||||
|
||||
return (
|
||||
<>
|
||||
{isLoading && <LinearProgress style={{ width: '100%' }} />}
|
||||
{users?.map(({ ID, NAME, GROUPS }, index) => (
|
||||
{users?.map(({ NAME, GROUPS }, index) => (
|
||||
<Card key={`user-${index}`} className={classes.card}>
|
||||
<CardContent>
|
||||
<Box display="flex" alignItems="center">
|
||||
<Typography className={classes.title}>{NAME}</Typography>
|
||||
{[GROUPS?.ID ?? []].flat().map(id => {
|
||||
const group = getGroupById(id);
|
||||
{[GROUPS?.ID ?? []].flat().map(ID => {
|
||||
const group = getGroupById(ID);
|
||||
return group ? (
|
||||
<Chip
|
||||
style={{ margin: '0 0.5em' }}
|
||||
key={`group-${index}-${id}`}
|
||||
key={`group-${index}-${ID}`}
|
||||
size="small"
|
||||
color="primary"
|
||||
clickable
|
||||
@ -80,8 +80,8 @@ function Users() {
|
||||
);
|
||||
}
|
||||
|
||||
Users.propTypes = {};
|
||||
ApplicationDeploy.propTypes = {};
|
||||
|
||||
Users.defaultProps = {};
|
||||
ApplicationDeploy.defaultProps = {};
|
||||
|
||||
export default Users;
|
||||
export default ApplicationDeploy;
|
@ -16,21 +16,12 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
function Groups() {
|
||||
|
||||
return (
|
||||
<div>
|
||||
Groups
|
||||
</div>
|
||||
);
|
||||
function ApplicationManage() {
|
||||
return <div>Manage</div>;
|
||||
}
|
||||
|
||||
Groups.propTypes = {
|
||||
name: PropTypes.string
|
||||
};
|
||||
ApplicationManage.propTypes = {};
|
||||
|
||||
Groups.defaultProps = {
|
||||
name: ''
|
||||
};
|
||||
ApplicationManage.defaultProps = {};
|
||||
|
||||
export default Groups;
|
||||
export default ApplicationManage;
|
@ -1,5 +1,5 @@
|
||||
import Create from 'client/containers/Application/Create';
|
||||
import Deploy from 'client/containers/Application/Deploy';
|
||||
import Manage from 'client/containers/Application/Manage';
|
||||
import ApplicationCreate from 'client/containers/Application/Create';
|
||||
import ApplicationDeploy from 'client/containers/Application/Deploy';
|
||||
import ApplicationManage from 'client/containers/Application/Manage';
|
||||
|
||||
export { Create, Deploy, Manage };
|
||||
export { ApplicationCreate, ApplicationDeploy, ApplicationManage };
|
||||
|
@ -15,14 +15,9 @@
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { makeStyles, Box, Typography } from '@material-ui/core';
|
||||
import { Box, Typography } from '@material-ui/core';
|
||||
|
||||
const dashboardStyles = makeStyles(theme => ({
|
||||
root: {},
|
||||
title: {
|
||||
color: theme.palette.common.black
|
||||
}
|
||||
}));
|
||||
import dashboardStyles from 'client/containers/Dashboard/styles';
|
||||
|
||||
function Dashboard() {
|
||||
const classes = dashboardStyles();
|
||||
|
8
src/fireedge/src/public/containers/Dashboard/styles.js
Normal file
8
src/fireedge/src/public/containers/Dashboard/styles.js
Normal file
@ -0,0 +1,8 @@
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
|
||||
export default makeStyles(theme => ({
|
||||
root: {},
|
||||
title: {
|
||||
color: theme.palette.common.black
|
||||
}
|
||||
}));
|
@ -25,9 +25,9 @@ import {
|
||||
|
||||
import useAuth from 'client/hooks/useAuth';
|
||||
|
||||
import FormUser from 'client/containers/Login/FormUser';
|
||||
import Form2fa from 'client/containers/Login/Form2fa';
|
||||
import FormGroup from 'client/containers/Login/FormGroup';
|
||||
import FormUser from 'client/containers/Login/Forms/FormUser';
|
||||
import Form2fa from 'client/containers/Login/Forms/Form2fa';
|
||||
import FormGroup from 'client/containers/Login/Forms/FormGroup';
|
||||
import loginStyles from 'client/containers/Login/styles';
|
||||
import Logo from 'client/icons/logo';
|
||||
|
||||
|
@ -1,4 +0,0 @@
|
||||
import Groups from './Groups';
|
||||
import Users from './Users';
|
||||
|
||||
export { Groups, Users };
|
@ -2,6 +2,7 @@ import { makeStyles } from '@material-ui/core';
|
||||
|
||||
export default makeStyles(() => ({
|
||||
root: {
|
||||
minHeight: '100%'
|
||||
minHeight: '100%',
|
||||
width: '100%'
|
||||
}
|
||||
}));
|
||||
|
@ -7,7 +7,7 @@ import { fakeDelay } from 'client/utils/helpers';
|
||||
|
||||
import * as serviceAuth from 'client/services/auth';
|
||||
import * as serviceUsers from 'client/services/users';
|
||||
import * as serviceGroups from 'client/services/groups';
|
||||
import * as servicePool from 'client/services/pool';
|
||||
import {
|
||||
startAuth,
|
||||
selectFilterGroup,
|
||||
@ -80,7 +80,7 @@ export default function useAuth() {
|
||||
return serviceAuth
|
||||
.getUser()
|
||||
.then(user => dispatch(successAuth({ user })))
|
||||
.then(serviceGroups.getGroups)
|
||||
.then(servicePool.getGroups)
|
||||
.then(groups => dispatch(setGroups(groups)))
|
||||
.catch(err => dispatch(failureAuth({ error: err })));
|
||||
}, [dispatch, jwtName, authUser]);
|
||||
|
@ -1,43 +1,80 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useSelector, useDispatch, shallowEqual } from 'react-redux';
|
||||
|
||||
import {
|
||||
setGroups,
|
||||
setUsers,
|
||||
import actions, {
|
||||
startOneRequest,
|
||||
failureOneRequest
|
||||
} from 'client/actions/pool';
|
||||
|
||||
import * as servicesGroups from 'client/services/groups';
|
||||
import * as servicesUsers from 'client/services/users';
|
||||
import * as servicePool from 'client/services/pool';
|
||||
|
||||
export default function useOpennebula() {
|
||||
const dispatch = useDispatch();
|
||||
const { groups, users } = useSelector(
|
||||
state => state?.Opennebula,
|
||||
const {
|
||||
groups,
|
||||
users,
|
||||
vNetworks,
|
||||
vNetworksTemplates,
|
||||
templates,
|
||||
filterPool: filter
|
||||
} = useSelector(
|
||||
state => ({
|
||||
...state?.Opennebula,
|
||||
filterPool: state?.Authenticated?.filterPool
|
||||
}),
|
||||
shallowEqual
|
||||
);
|
||||
|
||||
const getGroups = useCallback(() => {
|
||||
dispatch(startOneRequest());
|
||||
return servicesGroups
|
||||
.getGroups()
|
||||
.then(data => dispatch(setGroups(data)))
|
||||
return servicePool
|
||||
.getGroups({ filter })
|
||||
.then(data => dispatch(actions.setGroups(data)))
|
||||
.catch(err => dispatch(failureOneRequest({ error: err })));
|
||||
}, [dispatch]);
|
||||
}, [dispatch, filter]);
|
||||
|
||||
const getUsers = useCallback(() => {
|
||||
dispatch(startOneRequest());
|
||||
return servicesUsers
|
||||
.getUsers()
|
||||
.then(data => dispatch(setUsers(data)))
|
||||
return servicePool
|
||||
.getUsers({ filter })
|
||||
.then(data => dispatch(actions.setUsers(data)))
|
||||
.catch(err => dispatch(failureOneRequest({ error: err })));
|
||||
}, [dispatch]);
|
||||
}, [dispatch, filter]);
|
||||
|
||||
const getVNetworks = useCallback(() => {
|
||||
dispatch(startOneRequest());
|
||||
return servicePool
|
||||
.getVNetworks({ filter })
|
||||
.then(data => dispatch(actions.setVNetworks(data)))
|
||||
.catch(err => dispatch(failureOneRequest({ error: err })));
|
||||
}, [dispatch, filter]);
|
||||
|
||||
const getVNetworksTemplates = useCallback(() => {
|
||||
dispatch(startOneRequest());
|
||||
return servicePool
|
||||
.getVNetworksTemplates({ filter })
|
||||
.then(data => dispatch(actions.setVNetworkTemplates(data)))
|
||||
.catch(err => dispatch(failureOneRequest({ error: err })));
|
||||
}, [dispatch, filter]);
|
||||
|
||||
const getTemplates = useCallback(() => {
|
||||
dispatch(startOneRequest());
|
||||
return servicePool
|
||||
.getTemplates({ filter })
|
||||
.then(data => dispatch(actions.setTemplates(data)))
|
||||
.catch(err => dispatch(failureOneRequest({ error: err })));
|
||||
}, [dispatch, filter]);
|
||||
|
||||
return {
|
||||
groups,
|
||||
getGroups,
|
||||
users,
|
||||
getUsers
|
||||
getUsers,
|
||||
vNetworks,
|
||||
getVNetworks,
|
||||
vNetworksTemplates,
|
||||
getVNetworksTemplates,
|
||||
templates,
|
||||
getTemplates
|
||||
};
|
||||
}
|
||||
|
@ -27,8 +27,8 @@ const initial = {
|
||||
files: [],
|
||||
marketPlaces: [],
|
||||
apps: [],
|
||||
virtualNetworks: [],
|
||||
networkTemplates: [],
|
||||
vNetworks: [],
|
||||
vNetworksTemplates: [],
|
||||
securityGroups: [],
|
||||
clusters: [],
|
||||
hosts: [],
|
||||
|
@ -16,54 +16,50 @@
|
||||
import {
|
||||
Dashboard as DashboardIcon,
|
||||
Settings as SettingsIcon,
|
||||
Ballot as BallotIcon
|
||||
Ballot as BallotIcon,
|
||||
Palette as PaletteIcon,
|
||||
Reddit as RedditIcon,
|
||||
Build as BuildIcon
|
||||
} from '@material-ui/icons';
|
||||
|
||||
import Login from 'client/containers/Login';
|
||||
import { Clusters, Hosts, Zones } from 'client/containers/Infrastructure';
|
||||
import { Users, Groups } from 'client/containers/System';
|
||||
import Dashboard from 'client/containers/Dashboard';
|
||||
import Settings from 'client/containers/Settings';
|
||||
import TestApi from 'client/containers/TestApi';
|
||||
import Dashboard from 'client/containers/Dashboard';
|
||||
import {
|
||||
ApplicationCreate,
|
||||
ApplicationDeploy,
|
||||
ApplicationManage
|
||||
} from 'client/containers/Application';
|
||||
|
||||
export const PATH = {
|
||||
LOGIN: '/',
|
||||
DASHBOARD: '/dashboard',
|
||||
APPLICATION: {
|
||||
CREATE: '/application/create',
|
||||
MANAGE: '/application/manage',
|
||||
DEPLOY: '/application/deploy'
|
||||
},
|
||||
SETTINGS: '/settings',
|
||||
TEST_API: '/test-api',
|
||||
INFRASTRUCTURE: {
|
||||
CLUSTERS: '/clusters',
|
||||
HOSTS: '/hosts',
|
||||
ZONES: '/zones'
|
||||
},
|
||||
SYSTEM: {
|
||||
USERS: '/users',
|
||||
GROUPS: '/groups'
|
||||
},
|
||||
NETWORKS: {
|
||||
VNETS: '/vnets',
|
||||
VNETS_TEMPLATES: '/vnets-templates',
|
||||
VNETS_TOPOLOGY: '/vnets-topology',
|
||||
SEC_GROUPS: '/secgroups'
|
||||
}
|
||||
TEST_API: '/test-api'
|
||||
};
|
||||
|
||||
const ENDPOINTS = [
|
||||
{
|
||||
label: 'login',
|
||||
label: 'Login',
|
||||
path: PATH.LOGIN,
|
||||
authenticated: false,
|
||||
component: Login
|
||||
},
|
||||
{
|
||||
label: 'dashboard',
|
||||
label: 'Dashboard',
|
||||
path: PATH.DASHBOARD,
|
||||
authenticated: true,
|
||||
icon: DashboardIcon,
|
||||
component: Dashboard
|
||||
},
|
||||
{
|
||||
label: 'settings',
|
||||
label: 'Settings',
|
||||
path: PATH.SETTINGS,
|
||||
authenticated: true,
|
||||
header: true,
|
||||
@ -71,7 +67,7 @@ const ENDPOINTS = [
|
||||
component: Settings
|
||||
},
|
||||
{
|
||||
label: 'test api',
|
||||
label: 'Test API',
|
||||
path: PATH.TEST_API,
|
||||
authenticated: true,
|
||||
devMode: true,
|
||||
@ -79,75 +75,25 @@ const ENDPOINTS = [
|
||||
component: TestApi
|
||||
},
|
||||
{
|
||||
label: 'infrastructure',
|
||||
label: 'Create Application',
|
||||
path: PATH.APPLICATION.CREATE,
|
||||
authenticated: true,
|
||||
icon: BallotIcon,
|
||||
routes: [
|
||||
{
|
||||
label: 'clusters',
|
||||
path: PATH.INFRASTRUCTURE.CLUSTERS,
|
||||
authenticated: true,
|
||||
component: Clusters
|
||||
},
|
||||
{
|
||||
label: 'hosts',
|
||||
path: PATH.INFRASTRUCTURE.HOSTS,
|
||||
authenticated: true,
|
||||
component: Hosts
|
||||
},
|
||||
{
|
||||
label: 'zones',
|
||||
path: PATH.INFRASTRUCTURE.ZONES,
|
||||
authenticated: true,
|
||||
component: Zones
|
||||
}
|
||||
]
|
||||
icon: PaletteIcon,
|
||||
component: ApplicationCreate
|
||||
},
|
||||
{
|
||||
label: 'system',
|
||||
label: 'Deploy Application',
|
||||
path: PATH.APPLICATION.DEPLOY,
|
||||
authenticated: true,
|
||||
icon: BallotIcon,
|
||||
routes: [
|
||||
{
|
||||
label: 'users',
|
||||
path: PATH.SYSTEM.USERS,
|
||||
authenticated: true,
|
||||
component: Users
|
||||
},
|
||||
{
|
||||
label: 'groups',
|
||||
path: PATH.SYSTEM.GROUPS,
|
||||
authenticated: true,
|
||||
component: Groups
|
||||
}
|
||||
]
|
||||
icon: RedditIcon,
|
||||
component: ApplicationDeploy
|
||||
},
|
||||
{
|
||||
label: 'networks',
|
||||
label: 'Manage Application',
|
||||
path: PATH.APPLICATION.MANAGE,
|
||||
authenticated: true,
|
||||
icon: BallotIcon,
|
||||
routes: [
|
||||
{
|
||||
label: 'vnets',
|
||||
path: PATH.NETWORKS.VNETS,
|
||||
authenticated: true
|
||||
},
|
||||
{
|
||||
label: 'vnets templates',
|
||||
path: PATH.NETWORKS.VNETS_TEMPLATES,
|
||||
authenticated: true
|
||||
},
|
||||
{
|
||||
label: 'vnets topology',
|
||||
path: PATH.NETWORKS.VNETS_TOPOLOGY,
|
||||
authenticated: true
|
||||
},
|
||||
{
|
||||
label: 'vnets secgroup',
|
||||
path: PATH.NETWORKS.SEC_GROUPS,
|
||||
authenticated: true
|
||||
}
|
||||
]
|
||||
icon: BuildIcon,
|
||||
component: ApplicationManage
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -2,17 +2,4 @@ import { Actions, Commands } from 'server/utils/constants/commands/group';
|
||||
import { requestData, requestParams } from 'client/utils';
|
||||
import httpCodes from 'server/utils/constants/http-codes';
|
||||
|
||||
export const getGroups = () => {
|
||||
const name = Actions.GROUP_POOL_INFO;
|
||||
const { url, options } = requestParams({}, { name, ...Commands[name] });
|
||||
|
||||
return requestData(url, options).then(res => {
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res;
|
||||
|
||||
return [res?.data?.GROUP_POOL?.GROUP ?? []].flat();
|
||||
});
|
||||
};
|
||||
|
||||
export default {
|
||||
getGroups
|
||||
};
|
||||
export default {};
|
||||
|
86
src/fireedge/src/public/services/pool.js
Normal file
86
src/fireedge/src/public/services/pool.js
Normal file
@ -0,0 +1,86 @@
|
||||
import User from 'server/utils/constants/commands/user';
|
||||
import Group from 'server/utils/constants/commands/group';
|
||||
import VNet from 'server/utils/constants/commands/vn';
|
||||
import VNetTemplate from 'server/utils/constants/commands/vntemplate';
|
||||
import Template from 'server/utils/constants/commands/template';
|
||||
|
||||
import httpCodes from 'server/utils/constants/http-codes';
|
||||
import { requestData, requestParams } from 'client/utils';
|
||||
|
||||
export const getUsers = ({ filter }) => {
|
||||
const name = User.Actions.USER_POOL_INFO;
|
||||
const { url, options } = requestParams(
|
||||
{ filter },
|
||||
{ name, ...User.Commands[name] }
|
||||
);
|
||||
|
||||
return requestData(url, options).then(res => {
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res;
|
||||
|
||||
return [res?.data?.USER_POOL?.USER ?? []].flat();
|
||||
});
|
||||
};
|
||||
|
||||
export const getGroups = ({ filter }) => {
|
||||
const name = Group.Actions.GROUP_POOL_INFO;
|
||||
const { url, options } = requestParams(
|
||||
{ filter },
|
||||
{ name, ...Group.Commands[name] }
|
||||
);
|
||||
|
||||
return requestData(url, options).then(res => {
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res;
|
||||
|
||||
return [res?.data?.GROUP_POOL?.GROUP ?? []].flat();
|
||||
});
|
||||
};
|
||||
|
||||
export const getVNetworks = ({ filter }) => {
|
||||
const name = VNet.Actions.VN_POOL_INFO;
|
||||
const { url, options } = requestParams(
|
||||
{ filter },
|
||||
{ name, ...VNet.Commands[name] }
|
||||
);
|
||||
|
||||
return requestData(url, options).then(res => {
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res;
|
||||
|
||||
return [res?.data?.VNET_POOL?.VNET ?? []].flat();
|
||||
});
|
||||
};
|
||||
|
||||
export const getVNetworksTemplates = ({ filter }) => {
|
||||
const name = VNetTemplate.Actions.VNTEMPLATE_POOL_INFO;
|
||||
const { url, options } = requestParams(
|
||||
{ filter },
|
||||
{ name, ...VNetTemplate.Commands[name] }
|
||||
);
|
||||
|
||||
return requestData(url, options).then(res => {
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res;
|
||||
|
||||
return [res?.data?.VNTEMPLATE_POOL?.VNTEMPLATE ?? []].flat();
|
||||
});
|
||||
};
|
||||
|
||||
export const getTemplates = ({ filter }) => {
|
||||
const name = Template.Actions.TEMPLATE_POOL_INFO;
|
||||
const { url, options } = requestParams(
|
||||
{ filter },
|
||||
{ name, ...Template.Commands[name] }
|
||||
);
|
||||
|
||||
return requestData(url, options).then(res => {
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res;
|
||||
|
||||
return [res?.data?.VMTEMPLATE_POOL?.VMTEMPLATE ?? []].flat();
|
||||
});
|
||||
};
|
||||
|
||||
export default {
|
||||
getUsers,
|
||||
getGroups,
|
||||
getVNetworks,
|
||||
getVNetworksTemplates,
|
||||
getTemplates
|
||||
};
|
@ -13,18 +13,6 @@ export const changeGroup = values => {
|
||||
});
|
||||
};
|
||||
|
||||
export const getUsers = () => {
|
||||
const name = Actions.USER_POOL_INFO;
|
||||
const { url, options } = requestParams({}, { name, ...Commands[name] });
|
||||
|
||||
return requestData(url, options).then(res => {
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res;
|
||||
|
||||
return [res?.data?.USER_POOL?.USER ?? []].flat();
|
||||
});
|
||||
};
|
||||
|
||||
export default {
|
||||
changeGroup,
|
||||
getUsers
|
||||
changeGroup
|
||||
};
|
||||
|
@ -4,7 +4,9 @@ import { from as resourceFrom } from 'server/utils/constants/defaults';
|
||||
|
||||
export const getQueries = params =>
|
||||
Object.entries(params)
|
||||
?.filter(([, { from }]) => from === resourceFrom.query)
|
||||
?.filter(([, { from, value }]) =>
|
||||
Boolean(from === resourceFrom.query && value)
|
||||
)
|
||||
?.map(([name, { value }]) => `${name}=${encodeURI(value)}`)
|
||||
?.join('&');
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user