mirror of
https://github.com/OpenNebula/one.git
synced 2024-12-23 17:33:56 +03:00
parent
721bc8fa16
commit
7975f64054
@ -1,4 +1,5 @@
|
||||
const CHANGE_ZONE = 'CHANGE_ZONE'
|
||||
const CHANGE_THEME_TYPE = 'CHANGE_THEME_TYPE'
|
||||
const CHANGE_LOADING = 'CHANGE_LOADING'
|
||||
const TOGGLE_MENU = 'TOGGLE_MENU'
|
||||
const FIX_MENU = 'FIX_MENU'
|
||||
@ -9,6 +10,7 @@ const REMOVE_SNACKBAR = 'REMOVE_SNACKBAR'
|
||||
|
||||
const Actions = {
|
||||
CHANGE_ZONE,
|
||||
CHANGE_THEME_TYPE,
|
||||
CHANGE_LOADING,
|
||||
TOGGLE_MENU,
|
||||
FIX_MENU,
|
||||
@ -23,6 +25,15 @@ module.exports = {
|
||||
type: CHANGE_ZONE,
|
||||
payload: { zone }
|
||||
}),
|
||||
updateTheme: (dispatch, getState) => {
|
||||
const current = getState()
|
||||
const currentTheme = current.Authenticated?.theme
|
||||
const userTheme = current.Authenticated?.user?.TEMPLATE?.FIREEDGE?.THEME ?? 'dark'
|
||||
|
||||
if (['dark', 'light'].includes(userTheme) && currentTheme !== userTheme) {
|
||||
dispatch(({ type: CHANGE_THEME_TYPE, payload: { theme: userTheme } }))
|
||||
}
|
||||
},
|
||||
changeLoading: isLoading => ({
|
||||
type: CHANGE_LOADING,
|
||||
payload: { isLoading }
|
||||
|
@ -1,10 +1,15 @@
|
||||
export default {
|
||||
export default (mode = 'dark') => ({
|
||||
palette: {
|
||||
type: 'dark',
|
||||
common: { black: '#000000', white: '#ffffff' },
|
||||
type: mode,
|
||||
common: {
|
||||
black: '#000000',
|
||||
white: '#ffffff'
|
||||
},
|
||||
background: {
|
||||
paper: '#2a2d3d',
|
||||
default: '#222431'
|
||||
paper: mode === 'dark' ? '#2e3440' : '#ffffff',
|
||||
// PREV paper: mode === 'dark' ? '#2a2d3d' : '#eceff4',
|
||||
default: mode === 'dark' ? '#242933' : '#f2f4f8'
|
||||
// PREV default: mode === 'dark' ? '#222431' : '#d8dee9'
|
||||
},
|
||||
primary: {
|
||||
light: '#2a2d3d',
|
||||
@ -28,4 +33,4 @@ export default {
|
||||
main: '#7b7c7e'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -8,10 +8,10 @@ import SelectCard from 'client/components/Cards/SelectCard'
|
||||
import { StatusBadge, StatusChip } from 'client/components/Status'
|
||||
import Datastore from 'client/constants/datastore'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
card: { backgroundColor: theme.palette.primary.main },
|
||||
const useStyles = makeStyles(() => ({
|
||||
title: { display: 'flex', gap: '0.5rem' }
|
||||
}))
|
||||
|
||||
const DatastoreCard = memo(
|
||||
({ value, isSelected, handleClick, actions }) => {
|
||||
const classes = useStyles()
|
||||
@ -23,7 +23,6 @@ const DatastoreCard = memo(
|
||||
return (
|
||||
<SelectCard
|
||||
stylesProps={{ minHeight: 160 }}
|
||||
cardProps={{ className: classes.card, elevation: 2 }}
|
||||
icon={
|
||||
<StatusBadge stateColor={state.color}>
|
||||
<DatastoreIcon />
|
||||
|
@ -8,8 +8,7 @@ import SelectCard from 'client/components/Cards/SelectCard'
|
||||
import { StatusBadge, StatusChip } from 'client/components/Status'
|
||||
import Host from 'client/constants/host'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
card: { backgroundColor: theme.palette.primary.main },
|
||||
const useStyles = makeStyles(() => ({
|
||||
title: { display: 'flex', gap: '0.5rem' }
|
||||
}))
|
||||
|
||||
@ -24,7 +23,6 @@ const HostCard = memo(
|
||||
return (
|
||||
<SelectCard
|
||||
stylesProps={{ minHeight: 160 }}
|
||||
cardProps={{ className: classes.card, elevation: 2 }}
|
||||
icon={
|
||||
<StatusBadge title={state?.name} stateColor={state.color}>
|
||||
<HostIcon />
|
||||
|
@ -1,23 +1,16 @@
|
||||
import React, { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
import NetworkIcon from '@material-ui/icons/AccountTree'
|
||||
import SelectCard from 'client/components/Cards/SelectCard'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
card: { backgroundColor: theme.palette.primary.main }
|
||||
}))
|
||||
|
||||
const NetworkCard = memo(
|
||||
({ value, isSelected, handleClick, actions }) => {
|
||||
const { ID, NAME } = value
|
||||
const classes = useStyles()
|
||||
|
||||
return (
|
||||
<SelectCard
|
||||
stylesProps={{ minHeight: 120 }}
|
||||
cardProps={{ className: classes.card, elevation: 2 }}
|
||||
icon={<NetworkIcon />}
|
||||
title={NAME}
|
||||
subheader={`#${ID}`}
|
||||
|
@ -5,7 +5,11 @@ import ProvidersIcon from '@material-ui/icons/Public'
|
||||
import SelectCard from 'client/components/Cards/SelectCard'
|
||||
|
||||
import { isExternalURL } from 'client/utils'
|
||||
import { PROVIDER_IMAGES_URL, PROVISION_IMAGES_URL } from 'client/constants'
|
||||
import {
|
||||
PROVIDER_IMAGES_URL,
|
||||
PROVISION_IMAGES_URL,
|
||||
DEFAULT_IMAGE
|
||||
} from 'client/constants'
|
||||
|
||||
const ProvisionTemplateCard = React.memo(
|
||||
({ value, isProvider, isSelected, handleClick }) => {
|
||||
@ -13,6 +17,10 @@ const ProvisionTemplateCard = React.memo(
|
||||
const { image } = isProvider ? plain : value
|
||||
const IMAGES_URL = isProvider ? PROVIDER_IMAGES_URL : PROVISION_IMAGES_URL
|
||||
|
||||
const onError = evt => {
|
||||
evt.target.src = evt.target.src === DEFAULT_IMAGE ? DEFAULT_IMAGE : ''
|
||||
}
|
||||
|
||||
const imgSource = React.useMemo(() =>
|
||||
isExternalURL(image) ? image : `${IMAGES_URL}/${image}`
|
||||
, [image])
|
||||
@ -27,7 +35,8 @@ const ProvisionTemplateCard = React.memo(
|
||||
mediaProps={image && {
|
||||
component: 'img',
|
||||
image: imgSource,
|
||||
draggable: false
|
||||
draggable: false,
|
||||
onError
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
@ -28,7 +28,7 @@ const SelectCard = memo(({
|
||||
observerOff,
|
||||
children
|
||||
}) => {
|
||||
const classes = selectCardStyles(stylesProps)
|
||||
const classes = selectCardStyles({ ...stylesProps, isSelected })
|
||||
const { isNearScreen, fromRef } = useNearScreen({
|
||||
distance: '100px'
|
||||
})
|
||||
@ -40,8 +40,7 @@ const SelectCard = memo(({
|
||||
{observerOff || isNearScreen ? (
|
||||
<Card
|
||||
className={clsx(classes.root, cardProps?.className, {
|
||||
[classes.actionArea]: !handleClick,
|
||||
[classes.selected]: isSelected
|
||||
[classes.actionArea]: !handleClick
|
||||
})}
|
||||
{...cardProps}
|
||||
>
|
||||
@ -50,10 +49,7 @@ const SelectCard = memo(({
|
||||
<ConditionalWrap
|
||||
condition={handleClick && !action}
|
||||
wrap={children =>
|
||||
<CardActionArea
|
||||
className={classes.actionArea}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<CardActionArea className={classes.actionArea} onClick={handleClick}>
|
||||
{children}
|
||||
</CardActionArea>
|
||||
}
|
||||
|
@ -1,47 +1,49 @@
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
|
||||
export default makeStyles(theme => ({
|
||||
root: {
|
||||
root: ({ isSelected }) => ({
|
||||
height: '100%',
|
||||
'& $actionArea, & $mediaActionArea, & $media': {
|
||||
transition: theme.transitions.create(
|
||||
['filter', 'background-color'],
|
||||
{ duration: '0.2s' }
|
||||
)
|
||||
}
|
||||
},
|
||||
selected: {
|
||||
color: theme.palette.secondary.contrastText,
|
||||
backgroundColor: theme.palette.secondary.main,
|
||||
'& .badge': {
|
||||
color: theme.palette.secondary.main,
|
||||
backgroundColor: theme.palette.secondary.contrastText
|
||||
transition: theme.transitions.create(
|
||||
['background-color', 'box-shadow'], { duration: '0.2s' }
|
||||
),
|
||||
'&:hover': {
|
||||
boxShadow: theme.shadows['5']
|
||||
},
|
||||
'& $subheader': { color: 'inherit' }
|
||||
},
|
||||
...(isSelected && {
|
||||
color: theme.palette.secondary.contrastText,
|
||||
backgroundColor: theme.palette.secondary.main,
|
||||
'& .badge': {
|
||||
color: theme.palette.secondary.main,
|
||||
backgroundColor: theme.palette.secondary.contrastText
|
||||
},
|
||||
'& $subheader': { color: 'inherit' }
|
||||
})
|
||||
}),
|
||||
actionArea: {
|
||||
height: '100%',
|
||||
minHeight: ({ minHeight = 140 }) => minHeight,
|
||||
'& $media': { filter: 'contrast(0) brightness(2)' }
|
||||
minHeight: ({ minHeight = 140 }) => minHeight
|
||||
},
|
||||
mediaActionArea: {
|
||||
display: 'flex',
|
||||
height: ({ mediaHeight = 140 }) => mediaHeight,
|
||||
'& $media': { filter: 'contrast(0) brightness(2)' },
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.primary.contrastText,
|
||||
backgroundColor: theme.palette.secondary.contrastText,
|
||||
'& $media': { filter: 'none' }
|
||||
}
|
||||
},
|
||||
media: {},
|
||||
media: {
|
||||
transition: theme.transitions.create('filter', { duration: '0.2s' }),
|
||||
filter: ({ isSelected }) => (theme.palette.type === 'dark' || isSelected)
|
||||
? 'contrast(0) brightness(2)'
|
||||
: 'contrast(0) brightness(0.8)'
|
||||
},
|
||||
headerRoot: { alignItems: 'end' },
|
||||
headerContent: { overflow: 'auto' },
|
||||
headerAvatar: {
|
||||
display: 'flex',
|
||||
color: theme.palette.primary.contrastText
|
||||
},
|
||||
header: {
|
||||
color: theme.palette.primary.contrastText
|
||||
color: ({ isSelected }) => isSelected
|
||||
? theme.palette.secondary.contrastText
|
||||
: theme.palette.text.primary
|
||||
},
|
||||
subheader: {
|
||||
overflow: 'hidden',
|
||||
|
@ -2,46 +2,51 @@ import * as React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import clsx from 'clsx'
|
||||
import { Paper, Typography, makeStyles, lighten } from '@material-ui/core'
|
||||
import { Paper, Typography, makeStyles, lighten, darken } from '@material-ui/core'
|
||||
import { addOpacityToColor } from 'client/utils'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
padding: '2em',
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
backgroundColor: ({ bgColor }) => bgColor
|
||||
},
|
||||
icon: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
right: 0,
|
||||
fontSize: '10em',
|
||||
fill: addOpacityToColor(theme.palette.common.white, 0.2)
|
||||
},
|
||||
wave: {
|
||||
display: 'block',
|
||||
position: 'absolute',
|
||||
opacity: 0.4,
|
||||
top: '-5%',
|
||||
left: '50%',
|
||||
width: 220,
|
||||
height: 220,
|
||||
borderRadius: '43%'
|
||||
},
|
||||
wave1: {
|
||||
backgroundColor: ({ bgColor }) => lighten(bgColor, 0.4),
|
||||
animation: '$drift 7s infinite linear'
|
||||
},
|
||||
wave2: {
|
||||
backgroundColor: ({ bgColor }) => lighten(bgColor, 0.6),
|
||||
animation: '$drift 5s infinite linear'
|
||||
},
|
||||
'@keyframes drift': {
|
||||
from: { transform: 'rotate(0deg)' },
|
||||
to: { transform: 'rotate(360deg)' }
|
||||
const useStyles = makeStyles(theme => {
|
||||
const getBackgroundColor = theme.palette.type === 'dark' ? darken : lighten
|
||||
const getContrastBackgroundColor = theme.palette.type === 'light' ? darken : lighten
|
||||
|
||||
return {
|
||||
root: {
|
||||
padding: '2em',
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
backgroundColor: ({ bgColor }) => getBackgroundColor(bgColor, 0.3)
|
||||
},
|
||||
icon: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
right: 0,
|
||||
fontSize: '10em',
|
||||
fill: addOpacityToColor(theme.palette.common.white, 0.2)
|
||||
},
|
||||
wave: {
|
||||
display: 'block',
|
||||
position: 'absolute',
|
||||
opacity: 0.4,
|
||||
top: '-5%',
|
||||
left: '50%',
|
||||
width: 220,
|
||||
height: 220,
|
||||
borderRadius: '43%'
|
||||
},
|
||||
wave1: {
|
||||
backgroundColor: ({ bgColor }) => getContrastBackgroundColor(bgColor, 0.3),
|
||||
animation: '$drift 7s infinite linear'
|
||||
},
|
||||
wave2: {
|
||||
backgroundColor: ({ bgColor }) => getContrastBackgroundColor(bgColor, 0.5),
|
||||
animation: '$drift 5s infinite linear'
|
||||
},
|
||||
'@keyframes drift': {
|
||||
from: { transform: 'rotate(0deg)' },
|
||||
to: { transform: 'rotate(360deg)' }
|
||||
}
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
const WavesCard = React.memo(({ text, value, bgColor, icon: Icon }) => {
|
||||
const classes = useStyles({ bgColor })
|
||||
|
@ -19,6 +19,7 @@ const useStyles = makeStyles(theme => ({
|
||||
padding: '0.5em 0',
|
||||
cursor: ({ isMoreThanMaxChars }) => isMoreThanMaxChars ? 'pointer' : 'default',
|
||||
fontFamily: 'monospace',
|
||||
color: '#fafafa',
|
||||
'&:hover': {
|
||||
background: '#333537'
|
||||
}
|
||||
@ -30,9 +31,6 @@ const useStyles = makeStyles(theme => ({
|
||||
time: {
|
||||
minWidth: '220px'
|
||||
},
|
||||
message: {
|
||||
color: '#fafafa'
|
||||
},
|
||||
[DEBUG_LEVEL.ERROR]: { borderLeft: `0.3em solid ${theme.palette.error.light}` },
|
||||
[DEBUG_LEVEL.WARN]: { borderLeft: `0.3em solid ${theme.palette.warning.light}` },
|
||||
[DEBUG_LEVEL.INFO]: { borderLeft: `0.3em solid ${theme.palette.info.light}` },
|
||||
@ -62,9 +60,9 @@ const Message = memo(({ timestamp, severity, message }) => {
|
||||
</div>
|
||||
<div className={classes.time}>{timestamp}</div>
|
||||
{(isCollapsed && isMoreThanMaxChars) ? (
|
||||
<div className={classes.message}>{`${message?.slice(0, MAX_CHARS)}…`}</div>
|
||||
<div>{`${message?.slice(0, MAX_CHARS)}…`}</div>
|
||||
) : (
|
||||
<div className={classes.message} dangerouslySetInnerHTML={{ __html: AnsiHtml(message) }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: AnsiHtml(message) }} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
@ -8,13 +8,26 @@ import {
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Typography
|
||||
Typography,
|
||||
makeStyles
|
||||
} from '@material-ui/core'
|
||||
|
||||
import SubmitButton from 'client/components/FormControl/SubmitButton'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
backgroundColor: theme.palette.background.default,
|
||||
width: '80%',
|
||||
height: '80%',
|
||||
[theme.breakpoints.only('xs')]: {
|
||||
width: '100%',
|
||||
height: '100%'
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
const DialogConfirmation = memo(
|
||||
({
|
||||
open,
|
||||
@ -28,6 +41,7 @@ const DialogConfirmation = memo(
|
||||
handleEntering,
|
||||
children
|
||||
}) => {
|
||||
const classes = useStyles()
|
||||
const isMobile = useMediaQuery(theme => theme.breakpoints.only('xs'))
|
||||
|
||||
return (
|
||||
@ -37,12 +51,9 @@ const DialogConfirmation = memo(
|
||||
open={open}
|
||||
onClose={handleCancel}
|
||||
maxWidth='lg'
|
||||
scroll="paper"
|
||||
PaperProps={{
|
||||
style: {
|
||||
height: isMobile ? '100%' : '80%',
|
||||
width: isMobile ? '100%' : '80%'
|
||||
}
|
||||
scroll='paper'
|
||||
classes={{
|
||||
paper: classes.root
|
||||
}}
|
||||
>
|
||||
<DialogTitle disableTypography>
|
||||
|
@ -34,7 +34,7 @@ export default makeStyles(theme => ({
|
||||
}
|
||||
},
|
||||
scrollable: {
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
paddingTop: theme.spacing(2),
|
||||
paddingBottom: theme.spacing(2),
|
||||
height: '100%',
|
||||
|
@ -103,7 +103,7 @@ const Tr = (str = '') => {
|
||||
return translate(key, valuesTr)
|
||||
}
|
||||
|
||||
const SelectTranslate = () => {
|
||||
const SelectTranslate = props => {
|
||||
const context = useContext(TranslateContext)
|
||||
const languages = Array.isArray(root?.langs) ? root?.langs : []
|
||||
|
||||
@ -120,6 +120,7 @@ const SelectTranslate = () => {
|
||||
fullWidth
|
||||
onChange={e => handleChange(e, context.changeLang)}
|
||||
defaultValue={context.lang}
|
||||
{...props}
|
||||
>
|
||||
{languages.map(({ key, value }) => (
|
||||
<option value={key} key={key}>
|
||||
|
@ -35,7 +35,8 @@ import headerStyles from 'client/components/Header/styles'
|
||||
|
||||
const Header = ({ title, scrollableContainer }) => {
|
||||
const { isOneAdmin } = useAuth()
|
||||
const { isFixMenu, fixMenu } = useGeneral()
|
||||
const { theme, isFixMenu, fixMenu } = useGeneral()
|
||||
|
||||
const isUpLg = useMediaQuery(theme => theme.breakpoints.up('lg'))
|
||||
const isMobile = useMediaQuery(theme => theme.breakpoints.only('xs'))
|
||||
|
||||
@ -78,7 +79,7 @@ const Header = ({ title, scrollableContainer }) => {
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
),
|
||||
[isFixMenu, fixMenu, isUpLg, isMobile, isOneAdmin, classes]
|
||||
[theme, isFixMenu, fixMenu, isUpLg, isMobile, isOneAdmin, classes]
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -47,11 +47,14 @@ export default makeStyles(theme => ({
|
||||
}
|
||||
},
|
||||
/* GROUP SWITCHER */
|
||||
modeThemeIcon: {
|
||||
color: theme.palette.primary.contrastText
|
||||
},
|
||||
/* GROUP SWITCHER */
|
||||
headerSwitcherLabel: { flexGrow: 1 },
|
||||
groupButton: { justifyContent: 'start' },
|
||||
groupSelectedIcon: {
|
||||
fontSize: '1rem',
|
||||
margin: theme.spacing(0, 2)
|
||||
}
|
||||
}
|
||||
))
|
||||
}))
|
||||
|
@ -27,6 +27,7 @@ const SidebarLink = ({ label, path, icon: Icon, devMode, isSubItem }) => {
|
||||
history.push(path)
|
||||
!isUpLg && fixMenu(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
button
|
||||
@ -38,7 +39,7 @@ const SidebarLink = ({ label, path, icon: Icon, devMode, isSubItem }) => {
|
||||
data-cy='main-menu-item'
|
||||
>
|
||||
{Icon && (
|
||||
<ListItemIcon className={classes.itemIcon}>
|
||||
<ListItemIcon>
|
||||
<Icon />
|
||||
</ListItemIcon>
|
||||
)}
|
||||
|
@ -75,7 +75,7 @@ const Sidebar = memo(({ endpoints }) => {
|
||||
className={classes.svg}
|
||||
/>
|
||||
<IconButton
|
||||
className={classes.itemIcon}
|
||||
className={classes.hamburger}
|
||||
onClick={handleSwapMenu}
|
||||
>
|
||||
{isUpLg ? <MenuIcon /> : <CloseIcon />}
|
||||
|
@ -6,7 +6,8 @@ export default makeStyles(theme => ({
|
||||
// CONTAINER MENU
|
||||
// -------------------------------
|
||||
drawerPaper: {
|
||||
backgroundColor: theme.palette.primary.light,
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
border: 'none',
|
||||
width: 0,
|
||||
visibility: 'hidden',
|
||||
whiteSpace: 'nowrap',
|
||||
@ -69,6 +70,10 @@ export default makeStyles(theme => ({
|
||||
// -------------------------------
|
||||
header: {
|
||||
userSelect: 'none',
|
||||
backgroundColor: theme.palette.type === 'dark'
|
||||
? theme.palette.background.paper
|
||||
: theme.palette.primary.main,
|
||||
color: theme.palette.text.primary,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
padding: '1rem',
|
||||
@ -116,7 +121,7 @@ export default makeStyles(theme => ({
|
||||
}
|
||||
},
|
||||
list: {
|
||||
color: theme.palette.primary.contrastText
|
||||
color: theme.palette.text.primary
|
||||
},
|
||||
expandIcon: {},
|
||||
subItemWrapper: {},
|
||||
@ -124,12 +129,11 @@ export default makeStyles(theme => ({
|
||||
paddingLeft: theme.spacing(4)
|
||||
},
|
||||
itemSelected: {
|
||||
color: theme.palette.secondary.main,
|
||||
color: theme.palette.text.primary,
|
||||
backgroundColor: theme.palette.primary.light,
|
||||
'&:hover': { backgroundColor: theme.palette.primary.light },
|
||||
'& $itemIcon': { color: theme.palette.secondary.main }
|
||||
'&:hover': { backgroundColor: theme.palette.primary.light }
|
||||
},
|
||||
itemIcon: {
|
||||
hamburger: {
|
||||
color: theme.palette.primary.contrastText
|
||||
}
|
||||
}))
|
||||
|
@ -1,21 +1,25 @@
|
||||
import React, { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { makeStyles, Typography, lighten } from '@material-ui/core'
|
||||
import { makeStyles, Typography, lighten, darken } from '@material-ui/core'
|
||||
import { addOpacityToColor } from 'client/utils'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: ({ stateColor = theme.palette.primary.main }) => ({
|
||||
color: lighten(stateColor, 0.75),
|
||||
backgroundColor: addOpacityToColor(stateColor, 0.2),
|
||||
cursor: 'default',
|
||||
padding: theme.spacing('0.25rem', '0.5rem'),
|
||||
borderRadius: 2,
|
||||
textTransform: 'uppercase',
|
||||
fontSize: theme.typography.overline.fontSize,
|
||||
fontWeight: theme.typography.fontWeightBold
|
||||
})
|
||||
}))
|
||||
const useStyles = makeStyles(theme => {
|
||||
const getBackgroundColor = theme.palette.type === 'dark' ? lighten : darken
|
||||
|
||||
return {
|
||||
root: ({ stateColor = theme.palette.primary.main }) => ({
|
||||
color: getBackgroundColor(stateColor, 0.75),
|
||||
backgroundColor: addOpacityToColor(stateColor, 0.2),
|
||||
cursor: 'default',
|
||||
padding: theme.spacing('0.25rem', '0.5rem'),
|
||||
borderRadius: 2,
|
||||
textTransform: 'uppercase',
|
||||
fontSize: theme.typography.overline.fontSize,
|
||||
fontWeight: theme.typography.fontWeightBold
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const StatusChip = memo(({ stateColor, children }) => {
|
||||
const classes = useStyles({ stateColor })
|
||||
|
@ -30,7 +30,11 @@ const TypographyWithPoint = ({ pointColor, children }) => {
|
||||
|
||||
TypographyWithPoint.propTypes = {
|
||||
pointColor: PropTypes.string,
|
||||
children: PropTypes.oneOfType([PropTypes.string, PropTypes.element])
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.array,
|
||||
PropTypes.element
|
||||
])
|
||||
}
|
||||
|
||||
TypographyWithPoint.defaultProps = {
|
||||
|
@ -21,7 +21,6 @@ const TotalProviders = () => {
|
||||
const chartData = React.useMemo(() => {
|
||||
const groups = groupBy(providers, 'TEMPLATE.PLAIN.provider')
|
||||
|
||||
console.log({ groups })
|
||||
return PROVIDERS_TYPES?.map(({ name, color }) => ({
|
||||
color,
|
||||
title: name,
|
||||
|
@ -62,6 +62,9 @@ module.exports = {
|
||||
/* sections */
|
||||
Dashboard: 'Dashboard',
|
||||
Settings: 'Settings',
|
||||
/* sections - settgins */
|
||||
Dark: 'Dark',
|
||||
Light: 'Light',
|
||||
/* sections - system */
|
||||
User: 'User',
|
||||
Users: 'Users',
|
||||
|
@ -14,11 +14,137 @@
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
import * as React from 'react'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
const Settings = () => (
|
||||
<div>{Tr(T.Settings)}</div>
|
||||
)
|
||||
import {
|
||||
makeStyles,
|
||||
Container,
|
||||
Paper,
|
||||
Typography,
|
||||
TextField
|
||||
} from '@material-ui/core'
|
||||
|
||||
import { useAuth, useGeneral } from 'client/hooks'
|
||||
import { Tr, TranslateContext } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
import SubmitButton from 'client/components/FormControl/SubmitButton'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
header: {
|
||||
paddingTop: '1rem'
|
||||
},
|
||||
title: {
|
||||
flexGrow: 1,
|
||||
letterSpacing: 0.1,
|
||||
fontWeight: 500
|
||||
},
|
||||
wrapper: {
|
||||
backgroundColor: theme.palette.background.default,
|
||||
maxWidth: 550,
|
||||
padding: '1rem'
|
||||
},
|
||||
subheader: {
|
||||
marginBottom: '1rem'
|
||||
},
|
||||
actions: {
|
||||
padding: '1rem 0',
|
||||
textAlign: 'end'
|
||||
}
|
||||
}))
|
||||
|
||||
const Settings = () => {
|
||||
const classes = useStyles()
|
||||
const context = React.useContext(TranslateContext)
|
||||
// const langAvailables = Array.isArray(window?.langs) ? window?.langs : []
|
||||
|
||||
const { theme: currentTheme } = useGeneral()
|
||||
const { updateUser } = useAuth()
|
||||
|
||||
const [{ theme, lang }, setSettings] = React.useState({
|
||||
theme: currentTheme,
|
||||
lang: context.lang
|
||||
})
|
||||
|
||||
const handleChange = evt => {
|
||||
evt.preventDefault()
|
||||
evt.persist()
|
||||
|
||||
setSettings(prev => ({
|
||||
...prev,
|
||||
[evt.target.name]: evt.target.value
|
||||
}))
|
||||
}
|
||||
|
||||
const handleSubmit = evt => {
|
||||
evt.preventDefault()
|
||||
|
||||
updateUser({
|
||||
template: `FIREEDGE = [\n THEME = "${theme}",\n LANG = "${lang}" ]\n`
|
||||
}).then(() => context.changeLang(lang))
|
||||
}
|
||||
|
||||
return (
|
||||
<Container disableGutters>
|
||||
<div className={classes.header}>
|
||||
<Typography variant='h5' className={classes.title}>
|
||||
{Tr(T.Settings)}
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<Paper className={classes.wrapper} variant='outlined'>
|
||||
<Typography variant='overline' component='div' className={classes.subheader}>
|
||||
{`${Tr(T.Configuration)} UI`}
|
||||
</Typography>
|
||||
<TextField
|
||||
id='select-theme-type'
|
||||
select
|
||||
fullWidth
|
||||
name='theme'
|
||||
color='secondary'
|
||||
label='Theme'
|
||||
value={theme}
|
||||
onChange={handleChange}
|
||||
SelectProps={{
|
||||
native: true
|
||||
}}
|
||||
variant='outlined'
|
||||
>
|
||||
<option value='light'>{T.Light}</option>
|
||||
<option value='dark'>{T.Dark}</option>
|
||||
</TextField>
|
||||
|
||||
{/* is not operative yet */}
|
||||
{/* <Select
|
||||
color='secondary'
|
||||
inputProps={{
|
||||
name: 'lang',
|
||||
id: 'select-lang',
|
||||
'data-cy': 'select-lang'
|
||||
}}
|
||||
onChange={handleChange}
|
||||
fullWidth
|
||||
native
|
||||
value={lang}
|
||||
variant='outlined'
|
||||
>
|
||||
{langAvailables.map(({ key, value }) => (
|
||||
<option value={key} key={key}>
|
||||
{value}
|
||||
</option>
|
||||
))}
|
||||
</Select> */}
|
||||
<div className={classes.actions}>
|
||||
<SubmitButton
|
||||
color='secondary'
|
||||
data-cy='settings-submit-button'
|
||||
label={Tr(T.Save)}
|
||||
onClick={handleSubmit}
|
||||
/>
|
||||
</div>
|
||||
</Paper>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
export default Settings
|
||||
|
@ -35,9 +35,9 @@ const ResponseForm = ({
|
||||
return (
|
||||
<>
|
||||
<Typography
|
||||
color="textPrimary"
|
||||
component="h2"
|
||||
variant="h2"
|
||||
color='textPrimary'
|
||||
component='h2'
|
||||
variant='h2'
|
||||
style={{ padding: '16px 0' }}
|
||||
>
|
||||
{name || 'Request'}
|
||||
@ -45,10 +45,10 @@ const ResponseForm = ({
|
||||
<Grid
|
||||
container
|
||||
spacing={3}
|
||||
justify="flex-start"
|
||||
component="form"
|
||||
justify='flex-start'
|
||||
component='form'
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
autoComplete="off"
|
||||
autoComplete='off'
|
||||
>
|
||||
{Object.entries(params)?.map(([nameCommand, { default: value }]) => (
|
||||
<Grid item xs={12} key={`param-${nameCommand}`}>
|
||||
@ -56,7 +56,7 @@ const ResponseForm = ({
|
||||
as={
|
||||
typeof value === 'boolean' ? (
|
||||
<FormControlLabel
|
||||
control={<Checkbox color="primary" />}
|
||||
control={<Checkbox color='primary' />}
|
||||
label={nameCommand}
|
||||
labelPlacement={nameCommand}
|
||||
/>
|
||||
@ -66,7 +66,8 @@ const ResponseForm = ({
|
||||
helperText={errors[name]?.message}
|
||||
fullWidth
|
||||
label={nameCommand}
|
||||
variant="outlined"
|
||||
color='secondary'
|
||||
variant='outlined'
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@ -36,12 +36,13 @@ const TestApi = () => {
|
||||
disableGutters
|
||||
style={{ display: 'flex', flexFlow: 'column', height: '100%' }}
|
||||
>
|
||||
<Grid container direction="row" spacing={2} className={classes.root}>
|
||||
<Grid container direction='row' spacing={2} className={classes.root}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<TextField
|
||||
fullWidth
|
||||
select
|
||||
variant="outlined"
|
||||
color='secondary'
|
||||
variant='outlined'
|
||||
label={Tr(T.SelectRequest)}
|
||||
value={name}
|
||||
onChange={handleChangeCommand}
|
||||
|
@ -15,8 +15,9 @@ import {
|
||||
logout as logoutRequest
|
||||
} from 'client/actions/user'
|
||||
import { setGroups } from 'client/actions/pool'
|
||||
import { updateTheme, enqueueError, enqueueSuccess } from 'client/actions/general'
|
||||
|
||||
export default function useAuth () {
|
||||
const useAuth = () => {
|
||||
const {
|
||||
jwt,
|
||||
error,
|
||||
@ -79,6 +80,7 @@ export default function useAuth () {
|
||||
.then(user => dispatch(successAuth({ user })))
|
||||
.then(serviceOne.getGroups)
|
||||
.then(groups => dispatch(setGroups(groups)))
|
||||
.then(() => dispatch(updateTheme))
|
||||
.catch(err => dispatch(failureAuth({ error: err })))
|
||||
}, [dispatch, JWT_NAME, authUser])
|
||||
|
||||
@ -105,11 +107,25 @@ export default function useAuth () {
|
||||
[dispatch, authUser]
|
||||
)
|
||||
|
||||
const updateUser = useCallback(
|
||||
({ template }) =>
|
||||
serviceOne
|
||||
.updateUser({ id: authUser.ID, template })
|
||||
.then(() => dispatch(enqueueSuccess(`User updated - ID: ${authUser.ID}`)))
|
||||
.then(getAuthInfo)
|
||||
.catch(err => {
|
||||
dispatch(enqueueError(err ?? 'Error update user'))
|
||||
throw err
|
||||
})
|
||||
, [dispatch, authUser]
|
||||
)
|
||||
|
||||
return {
|
||||
login,
|
||||
logout,
|
||||
getAuthInfo,
|
||||
setPrimaryGroup,
|
||||
updateUser,
|
||||
isLogged: !!jwt,
|
||||
authUser,
|
||||
isOneAdmin: authUser?.ID === ONEADMIN_ID,
|
||||
@ -120,3 +136,5 @@ export default function useAuth () {
|
||||
filterPool
|
||||
}
|
||||
}
|
||||
|
||||
export default useAuth
|
||||
|
@ -4,10 +4,13 @@ import { useSelector, useDispatch, shallowEqual } from 'react-redux'
|
||||
import * as actions from 'client/actions/general'
|
||||
|
||||
export default function useGeneral () {
|
||||
const { zone, isLoading, isOpenMenu, isFixMenu } = useSelector(
|
||||
state => state?.General,
|
||||
shallowEqual
|
||||
)
|
||||
const {
|
||||
zone,
|
||||
isLoading,
|
||||
isOpenMenu,
|
||||
isFixMenu,
|
||||
theme
|
||||
} = useSelector(state => state?.General, shallowEqual)
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const fixMenu = useCallback(isFixed => dispatch(actions.fixMenu(isFixed)), [
|
||||
@ -38,10 +41,11 @@ export default function useGeneral () {
|
||||
)
|
||||
|
||||
return {
|
||||
zone,
|
||||
theme,
|
||||
isFixMenu,
|
||||
isLoading,
|
||||
isOpenMenu,
|
||||
isFixMenu,
|
||||
zone,
|
||||
changeZone,
|
||||
openMenu,
|
||||
fixMenu,
|
||||
|
@ -1,25 +1,28 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import * as React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { CssBaseline, ThemeProvider, StylesProvider } from '@material-ui/core'
|
||||
import { createTheme, generateClassName } from 'client/theme'
|
||||
import { useGeneral } from 'client/hooks'
|
||||
|
||||
const MuiProvider = ({ theme: appTheme, children }) => {
|
||||
const [theme, setTheme] = useState(() => createTheme(appTheme))
|
||||
const { theme } = useGeneral()
|
||||
|
||||
useEffect(() => {
|
||||
const changeThemeType = () => createTheme(appTheme(theme))
|
||||
|
||||
const [muitheme, setTheme] = React.useState(changeThemeType)
|
||||
|
||||
React.useEffect(() => {
|
||||
const jssStyles = document.querySelector('#jss-server-side')
|
||||
if (jssStyles) {
|
||||
jssStyles.parentElement.removeChild(jssStyles)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
appTheme && setTheme(() => createTheme(appTheme))
|
||||
}, [appTheme])
|
||||
React.useEffect(() => { setTheme(changeThemeType) }, [theme])
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<ThemeProvider theme={muitheme}>
|
||||
<CssBaseline />
|
||||
<StylesProvider generateClassName={generateClassName}>
|
||||
{children}
|
||||
@ -29,7 +32,7 @@ const MuiProvider = ({ theme: appTheme, children }) => {
|
||||
}
|
||||
|
||||
MuiProvider.propTypes = {
|
||||
theme: PropTypes.object,
|
||||
theme: PropTypes.func,
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.node,
|
||||
PropTypes.arrayOf(PropTypes.node)
|
||||
@ -37,7 +40,7 @@ MuiProvider.propTypes = {
|
||||
}
|
||||
|
||||
MuiProvider.defaultProps = {
|
||||
theme: {},
|
||||
theme: () => {},
|
||||
children: undefined
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,8 @@ const initial = {
|
||||
notifications: [],
|
||||
isLoading: false,
|
||||
isOpenMenu: false,
|
||||
isFixMenu: false
|
||||
isFixMenu: false,
|
||||
theme: 'dark'
|
||||
}
|
||||
|
||||
const General = (state = initial, action) => {
|
||||
@ -57,6 +58,8 @@ const General = (state = initial, action) => {
|
||||
notification => notification.key !== action.key
|
||||
)
|
||||
}
|
||||
case GeneralActions.CHANGE_THEME_TYPE:
|
||||
return { ...state, ...action.payload }
|
||||
case GeneralActions.CHANGE_LOADING:
|
||||
return { ...state, ...action.payload }
|
||||
case GeneralActions.CHANGE_ZONE:
|
||||
|
@ -1,7 +1,8 @@
|
||||
import {
|
||||
Dashboard as DashboardIcon,
|
||||
Public as ProvidersIcon,
|
||||
SettingsSystemDaydream as ProvisionsIcon
|
||||
SettingsSystemDaydream as ProvisionsIcon,
|
||||
Settings as SettingsIcon
|
||||
} from '@material-ui/icons'
|
||||
|
||||
import Login from 'client/containers/Login'
|
||||
@ -46,13 +47,6 @@ export const ENDPOINTS = [
|
||||
icon: DashboardIcon,
|
||||
Component: Dashboard
|
||||
},
|
||||
{
|
||||
label: 'Settings',
|
||||
path: PATH.SETTINGS,
|
||||
authenticated: true,
|
||||
header: true,
|
||||
Component: Settings
|
||||
},
|
||||
{
|
||||
label: 'Providers',
|
||||
path: PATH.PROVIDERS.LIST,
|
||||
@ -92,6 +86,14 @@ export const ENDPOINTS = [
|
||||
path: PATH.PROVISIONS.EDIT,
|
||||
authenticated: true,
|
||||
Component: ProvisionCreateForm
|
||||
},
|
||||
{
|
||||
label: 'Settings',
|
||||
path: PATH.SETTINGS,
|
||||
authenticated: true,
|
||||
sidebar: true,
|
||||
icon: SettingsIcon,
|
||||
Component: Settings
|
||||
}
|
||||
]
|
||||
|
||||
|
@ -27,6 +27,17 @@ export const changeGroup = values => {
|
||||
})
|
||||
}
|
||||
|
||||
export const updateUser = values => {
|
||||
const name = Actions.USER_UPDATE
|
||||
const { url, options } = requestParams(values, { name, ...Commands[name] })
|
||||
|
||||
return requestData(url, options).then(res => {
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res
|
||||
|
||||
return res?.data ?? {}
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
changeGroup
|
||||
}
|
||||
|
@ -16,22 +16,22 @@
|
||||
const {
|
||||
from: { resource, postBody, query },
|
||||
httpMethod: { GET, POST, PUT, DELETE }
|
||||
} = require('../defaults');
|
||||
} = require('../defaults')
|
||||
|
||||
const USER_ALLOCATE = 'user.allocate';
|
||||
const USER_DELETE = 'user.delete';
|
||||
const USER_PASSWD = 'user.passwd';
|
||||
const USER_LOGIN = 'user.login';
|
||||
const USER_UPDATE = 'user.update';
|
||||
const USER_CHAUTH = 'user.chauth';
|
||||
const USER_QUOTA = 'user.quota';
|
||||
const USER_CHGRP = 'user.chgrp';
|
||||
const USER_ADDGROUP = 'user.addgroup';
|
||||
const USER_DELGROUP = 'user.delgroup';
|
||||
const USER_INFO = 'user.info';
|
||||
const USER_POOL_INFO = 'userpool.info';
|
||||
const USER_QUOTA_INFO = 'userquota.info';
|
||||
const USER_QUOTA_UPDATE = 'userquota.update';
|
||||
const USER_ALLOCATE = 'user.allocate'
|
||||
const USER_DELETE = 'user.delete'
|
||||
const USER_PASSWD = 'user.passwd'
|
||||
const USER_LOGIN = 'user.login'
|
||||
const USER_UPDATE = 'user.update'
|
||||
const USER_CHAUTH = 'user.chauth'
|
||||
const USER_QUOTA = 'user.quota'
|
||||
const USER_CHGRP = 'user.chgrp'
|
||||
const USER_ADDGROUP = 'user.addgroup'
|
||||
const USER_DELGROUP = 'user.delgroup'
|
||||
const USER_INFO = 'user.info'
|
||||
const USER_POOL_INFO = 'userpool.info'
|
||||
const USER_QUOTA_INFO = 'userquota.info'
|
||||
const USER_QUOTA_UPDATE = 'userquota.update'
|
||||
|
||||
const Actions = {
|
||||
USER_ALLOCATE,
|
||||
@ -48,7 +48,7 @@ const Actions = {
|
||||
USER_POOL_INFO,
|
||||
USER_QUOTA_INFO,
|
||||
USER_QUOTA_UPDATE
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Actions,
|
||||
@ -123,7 +123,7 @@ module.exports = {
|
||||
},
|
||||
[USER_UPDATE]: {
|
||||
// inspected
|
||||
httpMethod: POST,
|
||||
httpMethod: PUT,
|
||||
params: {
|
||||
id: {
|
||||
from: resource,
|
||||
@ -248,4 +248,4 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user