Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions src/fireedge/src/modules/components/Cards/WavesCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,19 @@ const Wave = styled('span')(({ theme, bgcolor, duration = 1 }) => {
})

const WavesCard = memo(
({ text, value, bgColor, icon: Icon, onClick }) => (
({ text, value, subtitle, bgColor, icon: Icon, onClick }) => (
<Card title={Tr(text)} bgcolor={bgColor} onClick={onClick || undefined}>
<Typography variant="h6" zIndex={2} noWrap>
<Translate word={text} />
</Typography>
<Typography variant="h4" zIndex={2}>
{value}
</Typography>
{subtitle && (
<Typography variant="body2" zIndex={2} sx={{ opacity: 0.85 }}>
{subtitle}
</Typography>
)}
<Wave bgcolor={bgColor} duration={7} />
<Wave bgcolor={bgColor} duration={5} />
{Icon && (
Expand All @@ -106,7 +111,7 @@ const WavesCard = memo(
)}
</Card>
),
(prev, next) => prev.value === next.value
(prev, next) => prev.value === next.value && prev.subtitle === next.subtitle
)

WavesCard.propTypes = {
Expand All @@ -116,6 +121,7 @@ WavesCard.propTypes = {
PropTypes.number,
PropTypes.element,
]),
subtitle: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
bgColor: PropTypes.string,
icon: PropTypes.any,
onClick: PropTypes.oneOfType([PropTypes.func, PropTypes.bool]),
Expand All @@ -124,6 +130,7 @@ WavesCard.propTypes = {
WavesCard.defaultProps = {
text: undefined,
value: undefined,
subtitle: undefined,
bgColor: '#ffffff00',
icon: undefined,
onClick: undefined,
Expand Down
15 changes: 15 additions & 0 deletions src/fireedge/src/modules/constants/translates.js
Original file line number Diff line number Diff line change
Expand Up @@ -2691,4 +2691,19 @@ module.exports = {
RunBackground: 'Run in background',

LearnMore: 'Learn more',

/* Admin Dashboard */
InfrastructureUtilization: 'Infrastructure Utilization',
StorageCapacity: 'Storage Capacity',
VmStateDistribution: 'VM State Distribution',
QuickActions: 'Quick Actions',
CreateVM: 'Create VM',
ViewHosts: 'View Hosts',
ViewDatastores: 'View Datastores',
NRunning: '%s running',
NMonitored: '%s monitored',
ImageDatastores: 'Image Datastores',
SystemDatastores: 'System Datastores',
FileDatastores: 'File Datastores',
NoDataAvailableYet: 'No data available yet',
}
182 changes: 62 additions & 120 deletions src/fireedge/src/modules/containers/Dashboard/Sunstone/General.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,58 +13,47 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { Box, CircularProgress, Grid } from '@mui/material'
import {
BoxIso as ImageIcon,
NetworkAlt as NetworkIcon,
EmptyPage as TemplatesIcon,
ModernTv as VmsIcons,
} from 'iconoir-react'
import { Box, Grid } from '@mui/material'
import PropTypes from 'prop-types'
import { ReactElement, memo, useEffect, useMemo } from 'react'
import { useHistory } from 'react-router-dom'
import { ReactElement, useEffect, useMemo } from 'react'

import {
ImageAPI,
VmAPI,
VmTemplateAPI,
VnAPI,
useAuth,
useGeneralApi,
useViews,
} from '@FeaturesModule'
import { useAuth, useGeneralApi, useViews } from '@FeaturesModule'
import { TranslateProvider } from '@ComponentsModule'
import { RESOURCE_NAMES } from '@ConstantsModule'
import { stringToBoolean } from '@ModelsModule'

import {
NumberEasing,
PATH,
TranslateProvider,
WavesCard,
} from '@ComponentsModule'
import { RESOURCE_NAMES, T, VM_POOL_PAGINATION_SIZE } from '@ConstantsModule'
import { stringToBoolean } from '@ModelsModule'
ResourceSummaryCards,
InfrastructureUtilization,
StorageCapacity,
HostMonitoringGraphs,
VmStateDistribution,
QuickActions,
} from './widgets'

const { VM, VM_TEMPLATE, IMAGE, VNET } = RESOURCE_NAMES
const { HOST, DATASTORE } = RESOURCE_NAMES

/**
* Sunstone admin dashboard with resource overview, utilization metrics,
* monitoring graphs, state distribution, and quick actions.
*
* @param {object} props - Props
* @param {object} props.view - View
* @param {string} props.view - Current view name
* @returns {ReactElement} Sunstone dashboard container
*/
export default function SunstoneDashboard({ view }) {
const { settings: { FIREEDGE: fireedge = {} } = {} } = useAuth()
const { DISABLE_ANIMATIONS } = fireedge
const { hasAccessToResource } = useViews()

// Delete second title
const { setSecondTitle } = useGeneralApi()
useEffect(() => setSecondTitle({}), [])

const { push: goTo } = useHistory()

const vmAccess = useMemo(() => hasAccessToResource(VM), [view])
const templateAccess = useMemo(() => hasAccessToResource(VM_TEMPLATE), [view])
const imageAccess = useMemo(() => hasAccessToResource(IMAGE), [view])
const vnetAccess = useMemo(() => hasAccessToResource(VNET), [view])
const hostAccess = useMemo(() => hasAccessToResource(HOST), [view])
const datastoreAccess = useMemo(
() => hasAccessToResource(DATASTORE),
[view]
)

const styles = useMemo(() => {
if (stringToBoolean(DISABLE_ANIMATIONS))
Expand All @@ -76,43 +65,45 @@ export default function SunstoneDashboard({ view }) {
return (
<TranslateProvider>
<Box py={3} sx={styles}>
<Grid
container
data-cy="dashboard-widget-total-sunstone-resources"
spacing={3}
>
<ResourceWidget
type="vms"
bgColor="#fa7892"
text={T.VMs}
icon={VmsIcons}
onClick={vmAccess && (() => goTo(PATH.INSTANCE.VMS.LIST))}
disableAnimations={DISABLE_ANIMATIONS}
/>
<ResourceWidget
type="vmtemples"
bgColor="#b25aff"
text={T.VMTemplates}
icon={TemplatesIcon}
onClick={templateAccess && (() => goTo(PATH.TEMPLATE.VMS.LIST))}
disableAnimations={DISABLE_ANIMATIONS}
/>
<ResourceWidget
type="images"
bgColor="#1fbbc6"
text={T.Images}
icon={ImageIcon}
onClick={imageAccess && (() => goTo(PATH.STORAGE.IMAGES.LIST))}
disableAnimations={DISABLE_ANIMATIONS}
/>
<ResourceWidget
type="vnets"
bgColor="#f09d42"
text={T.VirtualNetworks}
icon={NetworkIcon}
onClick={vnetAccess && (() => goTo(PATH.NETWORK.VNETS.LIST))}
disableAnimations={DISABLE_ANIMATIONS}
/>
{/* Row 1: Enhanced Resource Summary Cards */}
<ResourceSummaryCards
disableAnimations={DISABLE_ANIMATIONS}
view={view}
/>

{/* Row 2: Infrastructure Utilization + Storage Capacity */}
{(hostAccess || datastoreAccess) && (
<Grid container spacing={3} sx={{ mt: 0 }}>
{hostAccess && (
<Grid item xs={12} md={6}>
<InfrastructureUtilization view={view} />
</Grid>
)}
{datastoreAccess && (
<Grid item xs={12} md={6}>
<StorageCapacity view={view} />
</Grid>
)}
</Grid>
)}

{/* Row 3: Host CPU + Memory Monitoring Graphs */}
{hostAccess && (
<Box sx={{ mt: 3 }}>
<HostMonitoringGraphs />
</Box>
)}

{/* Row 4: VM State Distribution + Quick Actions */}
<Grid container spacing={3} sx={{ mt: 0 }}>
<Grid item xs={12} md={8}>
<VmStateDistribution
disableAnimations={stringToBoolean(DISABLE_ANIMATIONS)}
/>
</Grid>
<Grid item xs={12} md={4}>
<QuickActions view={view} />
</Grid>
</Grid>
</Box>
</TranslateProvider>
Expand All @@ -124,52 +115,3 @@ SunstoneDashboard.displayName = 'SunstoneDashboard'
SunstoneDashboard.propTypes = {
view: PropTypes.string,
}

const ResourceWidget = memo(
({ type = 'vms', onClick, text, bgColor, icon, disableAnimations }) => {
const options = {
vmtemples: VmTemplateAPI.useGetTemplatesQuery(undefined, {
skip: type !== 'vmtemples',
}),
images: ImageAPI.useGetImagesQuery(undefined, {
skip: type !== 'images',
}),
vnets: VnAPI.useGetVNetworksQuery(undefined, { skip: type !== 'vnets' }),
vms: VmAPI.useGetVmsPaginatedQuery(
{ extended: 0, pageSize: VM_POOL_PAGINATION_SIZE },
{ skip: type !== 'vms' }
),
}

const { data = [], isFetching } = options[type] || {}

const NumberElement = useMemo(() => {
if (stringToBoolean(disableAnimations)) return data?.length

return <NumberEasing value={data?.length} />
}, [disableAnimations, data?.length])

return (
<Grid item xs={12} sm={6} md={3}>
<WavesCard
bgColor={bgColor}
icon={icon}
text={text}
value={isFetching ? <CircularProgress size={20} /> : NumberElement}
onClick={onClick || undefined}
/>
</Grid>
)
}
)

ResourceWidget.displayName = 'ResourceWidget'

ResourceWidget.propTypes = {
type: PropTypes.string,
onClick: PropTypes.oneOfType([PropTypes.func, PropTypes.bool]),
text: PropTypes.string,
bgColor: PropTypes.string,
icon: PropTypes.any,
disableAnimations: PropTypes.string,
}
Loading