mirror of
https://github.com/OpenNebula/one.git
synced 2025-01-05 09:17:41 +03:00
B OpenNebula/one#6452: Translations review in FireEdge (#3121)
Signed-off-by: dcarracedo <dcarracedo@opennebula.io>
This commit is contained in:
parent
3b3fe1ce40
commit
3a691b7a98
@ -101,11 +101,11 @@ const HostCard = memo(
|
||||
</div>
|
||||
<div className={classes.caption}>
|
||||
<span>{`#${ID}`}</span>
|
||||
<span data-cy="cluster" title={`Cluster: ${CLUSTER}`}>
|
||||
<span data-cy="cluster" title={`${Tr(T.Cluster)}: ${CLUSTER}`}>
|
||||
<Server />
|
||||
<span>{` ${CLUSTER}`}</span>
|
||||
</span>
|
||||
<span title={`Running VMs: ${runningVms} / ${totalVms}`}>
|
||||
<span title={`${Tr(T.RunningVMs)}: ${runningVms} / ${totalVms}`}>
|
||||
<ModernTv />
|
||||
<span>{` ${runningVms} / ${totalVms}`}</span>
|
||||
</span>
|
||||
|
@ -104,12 +104,12 @@ const NicCard = memo(
|
||||
{
|
||||
text: stringToBoolean(RDP) && 'RDP',
|
||||
dataCy: `${dataCy}-rdp`,
|
||||
helpText: 'Remote Desktop Protocol',
|
||||
helpText: Tr(T['nic.card.rdp']),
|
||||
},
|
||||
{
|
||||
text: stringToBoolean(SSH) && 'SSH',
|
||||
dataCy: `${dataCy}-ssh`,
|
||||
helpText: 'Secure Shell Protocol',
|
||||
helpText: Tr(T['nic.card.ssh']),
|
||||
},
|
||||
hasAlias && {
|
||||
text: `ALIAS: ${aliasLength}`,
|
||||
|
@ -31,6 +31,7 @@ import {
|
||||
isRelative,
|
||||
} from 'client/models/Scheduler'
|
||||
import { sentenceCase } from 'client/utils'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
const StyledTypography = styled(Typography)(({ theme }) => ({
|
||||
marginLeft: `${theme.spacing(1)} !important`,
|
||||
@ -47,10 +48,11 @@ const ScheduleActionCard = memo(({ schedule, actions }) => {
|
||||
const { ID, ACTION, TIME, MESSAGE, DONE, WARNING, NAME } = schedule
|
||||
|
||||
const typeScheduleText =
|
||||
`${TEMPLATE_SCHEDULE_TYPE_STRING?.[getTypeScheduleAction(schedule)]}:` || ''
|
||||
Tr(TEMPLATE_SCHEDULE_TYPE_STRING?.[getTypeScheduleAction(schedule)]) +
|
||||
':' || ''
|
||||
|
||||
const titleName = NAME ? `(${NAME})` : ''
|
||||
const titleAction = `#${ID} ${sentenceCase(ACTION)} ${titleName}`
|
||||
const titleAction = `#${ID} ${Tr(sentenceCase(ACTION))} ${titleName}`
|
||||
const timeIsRelative = isRelative(TIME)
|
||||
|
||||
const time = timeIsRelative ? getPeriodicityByTimeInSeconds(TIME) : TIME
|
||||
@ -102,7 +104,9 @@ const ScheduleActionCard = memo(({ schedule, actions }) => {
|
||||
<>
|
||||
<StyledTypography variant="caption">
|
||||
{timeIsRelative ? (
|
||||
<span>{Object.values(time).join(' ')}</span>
|
||||
<span>
|
||||
{time?.time} {Tr(time?.period)}
|
||||
</span>
|
||||
) : (
|
||||
<span title={formatTime}>
|
||||
<Timer initial={TIME} />
|
||||
|
@ -21,8 +21,9 @@ import { Typography } from '@mui/material'
|
||||
|
||||
import MultipleTags from 'client/components/MultipleTags'
|
||||
import { rowStyles } from 'client/components/Tables/styles'
|
||||
import { SecurityGroup } from 'client/constants'
|
||||
import { SecurityGroup, T } from 'client/constants'
|
||||
import { getColorFromString, getUniqueLabels } from 'client/models/Helper'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
const getTotalOfResources = (resources) =>
|
||||
[resources?.ID ?? []].flat().length || 0
|
||||
@ -83,23 +84,23 @@ const SecurityGroupCard = memo(
|
||||
</div>
|
||||
<div className={classes.caption}>
|
||||
<span>{`#${ID}`}</span>
|
||||
<span title={`Owner: ${UNAME}`}>
|
||||
<span title={`${Tr(T.Owner)}: ${UNAME}`}>
|
||||
<User />
|
||||
<span data-cy="uname">{` ${UNAME}`}</span>
|
||||
</span>
|
||||
<span title={`Group: ${GNAME}`}>
|
||||
<span title={`${Tr(T.Group)}: ${GNAME}`}>
|
||||
<Group />
|
||||
<span data-cy="gname">{` ${GNAME}`}</span>
|
||||
</span>
|
||||
<span title={`Total updated VMs: ${totalUpdatedVms}`}>
|
||||
<span title={`${Tr(T.TotalUpdatedVms)}: ${totalUpdatedVms}`}>
|
||||
<PcCheck />
|
||||
<span>{` ${totalUpdatedVms}`}</span>
|
||||
</span>
|
||||
<span title={`Total outdated VMs: ${totalOutdatedVms}`}>
|
||||
<span title={`${Tr(T.TotalOutdatedVms)}: ${totalOutdatedVms}`}>
|
||||
<PcNoEntry />
|
||||
<span>{` ${totalOutdatedVms}`}</span>
|
||||
</span>
|
||||
<span title={`Total error VMs: ${totalErrorVms}`}>
|
||||
<span title={`${Tr(T.TotalErrorVms)}: ${totalErrorVms}`}>
|
||||
<PcWarning />
|
||||
<span>{` ${totalErrorVms}`}</span>
|
||||
</span>
|
||||
|
@ -19,7 +19,9 @@ import { Typography, Paper } from '@mui/material'
|
||||
|
||||
import { rowStyles } from 'client/components/Tables/styles'
|
||||
import * as Helper from 'client/models/Helper'
|
||||
import { Snapshot } from 'client/constants'
|
||||
import { Snapshot, T } from 'client/constants'
|
||||
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
const SnapshotCard = memo(
|
||||
({ snapshot, actions = [], extraActionProps = {} }) => {
|
||||
@ -29,7 +31,7 @@ const SnapshotCard = memo(
|
||||
const { SNAPSHOT_ID, NAME, TIME } = snapshot
|
||||
|
||||
const time = Helper.timeFromMilliseconds(+TIME)
|
||||
const timeAgo = `created ${time.toRelative()}`
|
||||
const timeAgo = `${Tr(T.Created)} ${time.toRelative()}`
|
||||
|
||||
return (
|
||||
<Paper
|
||||
|
@ -113,7 +113,7 @@ const UserCard = ({ user, rootProps }) => {
|
||||
>
|
||||
<Typography variant="caption">{`#${ID}`}</Typography>
|
||||
<Box display="flex" alignItems="center" mt={1}>
|
||||
<Tooltip title={`Group: ${GNAME}`}>
|
||||
<Tooltip title={`${Tr(T.Group)}: ${GNAME}`}>
|
||||
<Box display="flex" alignItems="center" mr={2}>
|
||||
<Group />
|
||||
<Typography variant="caption" ml={1}>
|
||||
@ -121,7 +121,7 @@ const UserCard = ({ user, rootProps }) => {
|
||||
</Typography>
|
||||
</Box>
|
||||
</Tooltip>
|
||||
<Tooltip title={`Auth Driver: ${AUTH_DRIVER}`}>
|
||||
<Tooltip title={`${Tr(T.AuthDriver)}: ${AUTH_DRIVER}`}>
|
||||
<Box display="flex" alignItems="center">
|
||||
<LockKey />
|
||||
<Typography
|
||||
@ -142,13 +142,13 @@ const UserCard = ({ user, rootProps }) => {
|
||||
<LinearProgressWithTooltip
|
||||
value={datastoreQuotaUsage.size.percentOfUsed}
|
||||
label={datastoreQuotaUsage.size.percentLabel}
|
||||
tooltipTitle="Datastore Size"
|
||||
tooltipTitle={T.DatastoreSize}
|
||||
icon={<HardDrive />}
|
||||
/>
|
||||
<LinearProgressWithTooltip
|
||||
value={vmQuotaUsage.vms.percentOfUsed}
|
||||
label={vmQuotaUsage.vms.percentLabel}
|
||||
tooltipTitle="VM Count"
|
||||
tooltipTitle={T.VMCount}
|
||||
icon={<ModernTv />}
|
||||
/>
|
||||
</Grid>
|
||||
@ -156,13 +156,13 @@ const UserCard = ({ user, rootProps }) => {
|
||||
<LinearProgressWithTooltip
|
||||
value={networkQuotaUsage.leases.percentOfUsed}
|
||||
label={networkQuotaUsage.leases.percentLabel}
|
||||
tooltipTitle="Network Leases"
|
||||
tooltipTitle={T.NetworkLeases}
|
||||
icon={<Network />}
|
||||
/>
|
||||
<LinearProgressWithTooltip
|
||||
value={imageQuotaUsage.rvms.percentOfUsed}
|
||||
label={imageQuotaUsage.rvms.percentLabel}
|
||||
tooltipTitle="Image RVMS"
|
||||
tooltipTitle={T.ImageRVMS}
|
||||
icon={<BoxIso />}
|
||||
/>
|
||||
</Grid>
|
||||
|
@ -170,7 +170,7 @@ const VirtualMachineCard = memo(
|
||||
<div className={classes.caption}>
|
||||
<span data-cy="id">{`#${ID}`}</span>
|
||||
<span title={timeFormat}>
|
||||
{`${+ETIME ? T.Done : T.Started} `}
|
||||
{`${+ETIME ? Tr(T.Done) : Tr(T.Started)} `}
|
||||
<Timer initial={time} />
|
||||
</span>
|
||||
<span title={`${Tr(T.VirtualCpu)}: ${VCPU}`}>
|
||||
|
@ -29,6 +29,7 @@ import {
|
||||
import { StatusCircle } from 'client/components/Status'
|
||||
import { getState } from 'client/models/VmGroup'
|
||||
import { T } from 'client/constants'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
/**
|
||||
* VmGroupCard component to display vmgroup details.
|
||||
@ -76,7 +77,7 @@ const VmGroupCard = ({ vmgroup, rootProps }) => {
|
||||
>
|
||||
<Typography variant="caption">{`#${ID}`}</Typography>
|
||||
<Box display="flex" alignItems="center" mt={1}>
|
||||
<Tooltip title={`Group: ${GNAME}`}>
|
||||
<Tooltip title={`${Tr(T.Group)}: ${GNAME}`}>
|
||||
<Box display="flex" alignItems="center" mr={2}>
|
||||
<Group />
|
||||
<Typography variant="caption" ml={1}>
|
||||
@ -192,7 +193,7 @@ const RolesComponent = ({ roles }) => {
|
||||
}}
|
||||
>
|
||||
<AccordionSummary>
|
||||
<Typography variant="caption">Affined Roles</Typography>
|
||||
<Typography variant="caption">{Tr(T.AffinedRoles)}</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails sx={{ overflow: 'hidden' }}>
|
||||
{renderRoles(affinedRoles)}
|
||||
@ -221,7 +222,7 @@ const RolesComponent = ({ roles }) => {
|
||||
}}
|
||||
>
|
||||
<AccordionSummary>
|
||||
<Typography variant="caption">Anti-Affined Roles</Typography>
|
||||
<Typography variant="caption">{Tr(T.AntiAffinedRoles)}</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>{renderRoles(antiAffinedRoles)}</AccordionDetails>
|
||||
</Accordion>
|
||||
|
@ -30,10 +30,13 @@ import {
|
||||
XAxis,
|
||||
YAxis,
|
||||
} from 'recharts'
|
||||
import { mapValues } from 'lodash'
|
||||
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { DataGridTable } from 'client/components/Tables'
|
||||
import { useTheme } from '@mui/material'
|
||||
import { CustomTooltip } from 'client/components/Tooltip'
|
||||
import { sentenceCase } from 'client/utils'
|
||||
|
||||
import {
|
||||
generateColorByMetric,
|
||||
@ -115,6 +118,16 @@ export const ChartRenderer = ({
|
||||
const ChartElement = ChartElements[coordinateType][chartType]
|
||||
const theme = useTheme()
|
||||
|
||||
// Map with translation for each metric
|
||||
const translationMap = mapValues(selectedMetrics, (value, key) => {
|
||||
const finalWord = key
|
||||
.split('_')
|
||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
||||
.join(' ')
|
||||
|
||||
return Tr(finalWord)
|
||||
})
|
||||
|
||||
const polarDataset = useMemo(
|
||||
() => (coordinateType === 'POLAR' ? FormatPolarDataset(datasets) : null),
|
||||
[coordinateType, datasets]
|
||||
@ -131,11 +144,17 @@ export const ChartRenderer = ({
|
||||
[coordinateType, chartType, datasets, polarDataset, paginatedData]
|
||||
)
|
||||
|
||||
// Translate columns in tables
|
||||
const tableColumnsTranslated = tableColumns?.map((column) => ({
|
||||
...column,
|
||||
headerName: Tr(column.headerName),
|
||||
}))
|
||||
|
||||
return (
|
||||
<ResponsiveContainer height="100%" width="100%">
|
||||
{chartType === CHART_TYPES.TABLE ? (
|
||||
<DataGridTable
|
||||
columns={tableColumns}
|
||||
columns={tableColumnsTranslated}
|
||||
data={datasets}
|
||||
selectedItems={selectedMetrics}
|
||||
/>
|
||||
@ -170,7 +189,7 @@ export const ChartRenderer = ({
|
||||
content={
|
||||
coordinateType === 'CARTESIAN' ? (
|
||||
<CustomTooltip
|
||||
labels={datasets.map((ds) => ds.label)}
|
||||
labels={datasets.map((ds) => Tr(sentenceCase(ds.label)))}
|
||||
generateColor={generateColorByMetric}
|
||||
formatMetric={humanReadableMetric}
|
||||
metricHues={metricHues}
|
||||
@ -191,14 +210,12 @@ export const ChartRenderer = ({
|
||||
(ds) => ds.id === parseInt(datasetId, 10)
|
||||
)
|
||||
|
||||
const datasetLabel = currentDataset.label
|
||||
|
||||
const lastSelectedMetric = [...currentDataset.metrics]
|
||||
.reverse()
|
||||
.find((m) => selectedMetrics[m.key])
|
||||
|
||||
if (lastSelectedMetric && metric === lastSelectedMetric.key) {
|
||||
return `${humanReadableMetric(metric)} (${datasetLabel})`
|
||||
return `${humanReadableMetric(metric)}`
|
||||
}
|
||||
|
||||
return humanReadableMetric(metric)
|
||||
@ -210,16 +227,7 @@ export const ChartRenderer = ({
|
||||
/>
|
||||
) : (
|
||||
<Legend
|
||||
formatter={(value) =>
|
||||
value
|
||||
.split('_')
|
||||
.map(
|
||||
(word) =>
|
||||
word.charAt(0).toUpperCase() +
|
||||
word.slice(1).toLowerCase()
|
||||
)
|
||||
.join(' ')
|
||||
}
|
||||
formatter={(value) => translationMap[value]}
|
||||
iconSize={12}
|
||||
layout="vertical"
|
||||
verticalAlign="middle"
|
||||
|
@ -22,6 +22,8 @@ import {
|
||||
exportDataToPDF,
|
||||
} from 'client/components/Charts/MultiChart/helpers/scripts'
|
||||
import { useGeneralApi } from 'client/features/General'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
/**
|
||||
* Renders a button that provides export options for data.
|
||||
@ -48,9 +50,7 @@ export const ExportButton = ({ data, exportOptions, exportHandlers }) => {
|
||||
if (exportHandlers[type]) {
|
||||
const error = exportHandlers[type](data)
|
||||
if (error) {
|
||||
enqueueError(
|
||||
'Error exporting data to ' + type.toUpperCase() + ': ' + error.message
|
||||
)
|
||||
enqueueError(T.ErrorExportingData, [type.toUpperCase(), error.message])
|
||||
}
|
||||
}
|
||||
handleMenuClose()
|
||||
@ -73,7 +73,7 @@ export const ExportButton = ({ data, exportOptions, exportHandlers }) => {
|
||||
},
|
||||
}}
|
||||
>
|
||||
Export
|
||||
{Tr(T.Export)}
|
||||
</Button>
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
@ -83,7 +83,7 @@ export const ExportButton = ({ data, exportOptions, exportHandlers }) => {
|
||||
>
|
||||
{exportOptions.map((option) => (
|
||||
<MenuItem key={option.type} onClick={() => handleExport(option.type)}>
|
||||
{option.label}
|
||||
{Tr(option.label)}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
@ -104,8 +104,8 @@ ExportButton.propTypes = {
|
||||
|
||||
ExportButton.defaultProps = {
|
||||
exportOptions: [
|
||||
{ type: 'csv', label: 'Export as CSV' },
|
||||
{ type: 'pdf', label: 'Export as PDF' },
|
||||
{ type: 'csv', label: T.ExportCSV },
|
||||
{ type: 'pdf', label: T.ExportPDF },
|
||||
],
|
||||
exportHandlers: {
|
||||
csv: (data) => exportDataToCSV(data),
|
||||
|
@ -17,7 +17,7 @@ import { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Box, Paper, Typography } from '@mui/material'
|
||||
import { isDevelopment } from 'client/utils'
|
||||
|
||||
import { Tr } from 'client/components/HOC'
|
||||
/**
|
||||
* Formats the input data for use in a polar chart.
|
||||
*
|
||||
@ -106,13 +106,15 @@ export const PolarTooltip = ({ active, payload }) => {
|
||||
}}
|
||||
>
|
||||
<Typography variant="body2" sx={{ fontWeight: 'bold', mb: 1 }}>
|
||||
{data.name
|
||||
.split('_')
|
||||
.map(
|
||||
(word) =>
|
||||
word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
|
||||
)
|
||||
.join(' ')}
|
||||
{Tr(
|
||||
data.name
|
||||
.split('_')
|
||||
.map(
|
||||
(word) =>
|
||||
word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
|
||||
)
|
||||
.join(' ')
|
||||
)}
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
{data?.uv} / {data?.pv}
|
||||
|
@ -24,7 +24,7 @@ import {
|
||||
GuacamoleSession, // eslint-disable-line no-unused-vars
|
||||
SOCKETS,
|
||||
GUACAMOLE_STATES_STR,
|
||||
// THUMBNAIL_UPDATE_FREQUENCY,
|
||||
T,
|
||||
} from 'client/constants'
|
||||
|
||||
const {
|
||||
@ -172,8 +172,8 @@ const GuacamoleClient = ({ id, display }) => {
|
||||
const isDisconnected = DISCONNECTED === stateString
|
||||
const isConnected = CONNECTED === stateString
|
||||
|
||||
isConnected && enqueueSuccess('Connection established')
|
||||
isDisconnected && enqueueInfo('Disconnected')
|
||||
isConnected && enqueueSuccess(T.SuccessConnectionEstablished)
|
||||
isDisconnected && enqueueInfo(T.InfoDisconnected)
|
||||
|
||||
!isDisconnect && setConnectionState({ state: stateString })
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ const HeaderVmInfo = ({ id, type }) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (isVMRC && isSuccess && vm && !isVCenter(vm)) {
|
||||
enqueueError(`${vm.ID} - ${vm.NAME} is not located on vCenter Host`)
|
||||
enqueueError(T.ErrorVmNoLocatedVenter, [vm.ID, vm.NAME])
|
||||
redirectTo(PATH.DASHBOARD)
|
||||
}
|
||||
}, [isVMRC, isSuccess])
|
||||
|
@ -17,7 +17,13 @@ import PropTypes from 'prop-types'
|
||||
import { Component, useState } from 'react'
|
||||
import { Box, TextField } from '@mui/material'
|
||||
import { DatePicker } from '@mui/lab'
|
||||
import { DateTime } from 'luxon'
|
||||
import { DateTime, Settings } from 'luxon'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
import LocalizationProvider from '@mui/lab/LocalizationProvider'
|
||||
import AdapterLuxon from '@mui/lab/AdapterLuxon'
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
|
||||
/**
|
||||
* DateRangeFilter component for selecting a date range.
|
||||
@ -35,6 +41,11 @@ export const DateRangeFilter = ({
|
||||
onDateChange,
|
||||
views,
|
||||
}) => {
|
||||
// Set language for date picker
|
||||
const { settings: { FIREEDGE: fireedge = {} } = {} } = useAuth()
|
||||
const lang = fireedge?.LANG?.substring(0, 2)
|
||||
Settings.defaultLocale = lang
|
||||
|
||||
const [dateRange, setDateRange] = useState({
|
||||
startDate: initialStartDate,
|
||||
endDate: initialEndDate,
|
||||
@ -56,27 +67,32 @@ export const DateRangeFilter = ({
|
||||
|
||||
return (
|
||||
<Box display="flex" alignItems="center" marginRight={2}>
|
||||
<DatePicker
|
||||
label="Start Date"
|
||||
value={dateRange.startDate}
|
||||
onChange={(date) => handleDateChange('startDate', date)}
|
||||
maxDate={dateRange.endDate || today}
|
||||
renderInput={(params) => (
|
||||
<TextField {...params} variant="outlined" margin="dense" />
|
||||
)}
|
||||
views={views}
|
||||
/>
|
||||
<Box marginLeft={2}>
|
||||
<LocalizationProvider dateAdapter={AdapterLuxon} locale={lang}>
|
||||
<DatePicker
|
||||
label="End Date"
|
||||
value={dateRange.endDate}
|
||||
onChange={(date) => handleDateChange('endDate', date)}
|
||||
minDate={dateRange.startDate || '1900-01-01'}
|
||||
label={Tr(T.StartDate)}
|
||||
value={dateRange.startDate}
|
||||
onChange={(date) => handleDateChange('startDate', date)}
|
||||
maxDate={dateRange.endDate || today}
|
||||
renderInput={(params) => (
|
||||
<TextField {...params} variant="outlined" margin="dense" />
|
||||
)}
|
||||
views={views}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
|
||||
<Box marginLeft={2}>
|
||||
<LocalizationProvider dateAdapter={AdapterLuxon} locale={lang}>
|
||||
<DatePicker
|
||||
label={Tr(T.EndDate)}
|
||||
value={dateRange.endDate}
|
||||
onChange={(date) => handleDateChange('endDate', date)}
|
||||
minDate={dateRange.startDate || '1900-01-01'}
|
||||
renderInput={(params) => (
|
||||
<TextField {...params} variant="outlined" margin="dense" />
|
||||
)}
|
||||
views={views}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
|
@ -151,7 +151,7 @@ const SelectController = memo(
|
||||
>
|
||||
{values?.map(({ text, value = '' }) => (
|
||||
<option key={`${name}-${value}`} value={value}>
|
||||
{text}
|
||||
{Tr(text)}
|
||||
</option>
|
||||
))}
|
||||
</TextField>
|
||||
|
@ -83,18 +83,18 @@ const TooltipComponent = ({ tooltip, tooltipLink, tooltipprops, children }) => {
|
||||
title={
|
||||
tooltipLink ? (
|
||||
<Typography variant="subtitle2">
|
||||
{tooltip}{' '}
|
||||
{Tr(tooltip)}{' '}
|
||||
<a
|
||||
className={classes.tooltipLink}
|
||||
target="_blank"
|
||||
href={tooltipLink.link}
|
||||
rel="noreferrer"
|
||||
>
|
||||
{tooltipLink.text}
|
||||
{Tr(tooltipLink.text)}
|
||||
</a>
|
||||
</Typography>
|
||||
) : (
|
||||
<Typography variant="subtitle2">{tooltip}</Typography>
|
||||
<Typography variant="subtitle2">{Tr(tooltip)}</Typography>
|
||||
)
|
||||
}
|
||||
{...tooltipprops}
|
||||
@ -127,7 +127,7 @@ const SubmitButton = memo(
|
||||
{isSubmitting && (
|
||||
<CircularProgress color="secondary" size={progressSize} />
|
||||
)}
|
||||
{!isSubmitting && (icon ?? label ?? Tr(T.Submit))}
|
||||
{!isSubmitting && (icon ?? Tr(label) ?? Tr(T.Submit))}
|
||||
</ButtonComponent>
|
||||
</TooltipComponent>
|
||||
)
|
||||
|
@ -25,6 +25,11 @@ import { Translate } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
import { generateKey } from 'client/utils'
|
||||
|
||||
import LocalizationProvider from '@mui/lab/LocalizationProvider'
|
||||
import AdapterLuxon from '@mui/lab/AdapterLuxon'
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { Settings } from 'luxon'
|
||||
|
||||
const TimeController = memo(
|
||||
({
|
||||
control,
|
||||
@ -35,6 +40,11 @@ const TimeController = memo(
|
||||
fieldProps: { defaultValue, ...fieldProps } = {},
|
||||
readOnly = false,
|
||||
}) => {
|
||||
// Set language for date picker
|
||||
const { settings: { FIREEDGE: fireedge = {} } = {} } = useAuth()
|
||||
const lang = fireedge?.LANG?.substring(0, 2)
|
||||
Settings.defaultLocale = lang
|
||||
|
||||
const {
|
||||
field: { value, ...controllerProps },
|
||||
fieldState: { error },
|
||||
@ -45,33 +55,35 @@ const TimeController = memo(
|
||||
: ['year', 'month', 'day', 'hours', 'minutes']
|
||||
|
||||
return (
|
||||
<DateTimePicker
|
||||
{...controllerProps}
|
||||
{...fieldProps}
|
||||
value={value}
|
||||
label={<Translate word={label} />}
|
||||
cancelText={<Translate word={T.Cancel} />}
|
||||
clearText={<Translate word={T.Clear} />}
|
||||
todayText={<Translate word={T.Today} />}
|
||||
InputProps={{
|
||||
readOnly,
|
||||
autoComplete: 'off',
|
||||
startAdornment: tooltip && <Tooltip title={tooltip} />,
|
||||
}}
|
||||
renderInput={({ inputProps, ...dateTimePickerProps }) => (
|
||||
<TextField
|
||||
{...dateTimePickerProps}
|
||||
fullWidth
|
||||
inputProps={{ ...inputProps, 'data-cy': cy }}
|
||||
error={Boolean(error)}
|
||||
helperText={
|
||||
Boolean(error) && <ErrorHelper label={error?.message} />
|
||||
}
|
||||
FormHelperTextProps={{ 'data-cy': `${cy}-error` }}
|
||||
/>
|
||||
)}
|
||||
views={views}
|
||||
/>
|
||||
<LocalizationProvider dateAdapter={AdapterLuxon} locale={lang}>
|
||||
<DateTimePicker
|
||||
{...controllerProps}
|
||||
{...fieldProps}
|
||||
value={value}
|
||||
label={<Translate word={label} />}
|
||||
cancelText={<Translate word={T.Cancel} />}
|
||||
clearText={<Translate word={T.Clear} />}
|
||||
todayText={<Translate word={T.Today} />}
|
||||
InputProps={{
|
||||
readOnly,
|
||||
autoComplete: 'off',
|
||||
startAdornment: tooltip && <Tooltip title={tooltip} />,
|
||||
}}
|
||||
renderInput={({ inputProps, ...dateTimePickerProps }) => (
|
||||
<TextField
|
||||
{...dateTimePickerProps}
|
||||
fullWidth
|
||||
inputProps={{ ...inputProps, 'data-cy': cy }}
|
||||
error={Boolean(error)}
|
||||
helperText={
|
||||
Boolean(error) && <ErrorHelper label={error?.message} />
|
||||
}
|
||||
FormHelperTextProps={{ 'data-cy': `${cy}-error` }}
|
||||
/>
|
||||
)}
|
||||
views={views}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
)
|
||||
},
|
||||
(prevProps, nextProps) =>
|
||||
|
@ -96,7 +96,7 @@ const ToggleController = memo(
|
||||
>
|
||||
{values?.map(({ text, value = '' }) => (
|
||||
<ToggleButton key={`${name}-${value}`} value={value} sx={{ p: 1 }}>
|
||||
{text}
|
||||
{Tr(text)}
|
||||
</ToggleButton>
|
||||
))}
|
||||
</ToggleButtonGroup>
|
||||
|
@ -22,7 +22,7 @@ import { GroupsTable, ClustersTable } from 'client/components/Tables'
|
||||
export const ACL_TYPE_ID_TRANSLATIONS = {
|
||||
INDIVIDUAL: {
|
||||
value: 'INDIVIDUAL',
|
||||
text: T['acls.form.create.resourcesUser.identifier'],
|
||||
text: T.Identifier,
|
||||
},
|
||||
GROUP: { value: 'GROUP', text: T.Group },
|
||||
ALL: { value: 'ALL', text: T.All },
|
||||
|
@ -20,7 +20,7 @@ import { string } from 'yup'
|
||||
/** @type {Field} Name field */
|
||||
const NAME = {
|
||||
name: 'NAME',
|
||||
label: T['marketplace.form.create.general.name'],
|
||||
label: T.Name,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
validation: string()
|
||||
.trim()
|
||||
@ -32,7 +32,7 @@ const NAME = {
|
||||
/** @type {Field} Name field */
|
||||
const DESCRIPTION = {
|
||||
name: 'DESCRIPTION',
|
||||
label: T['marketplace.form.create.general.description'],
|
||||
label: T.Description,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
validation: string()
|
||||
.trim()
|
||||
|
@ -29,7 +29,7 @@ import { useFieldArray, useForm, FormProvider } from 'react-hook-form'
|
||||
import { yupResolver } from '@hookform/resolvers/yup'
|
||||
|
||||
import { FormWithSchema, Legend } from 'client/components/Forms'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
import { Translate, Tr } from 'client/components/HOC'
|
||||
|
||||
import {
|
||||
FIELDS,
|
||||
@ -127,22 +127,22 @@ const RulesSection = memo(
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<b>{T.Protocol}</b>
|
||||
<b>{Tr(T.Protocol)}</b>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<b>{T.Type}</b>
|
||||
<b>{Tr(T.Type)}</b>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<b>{T.Range}</b>
|
||||
<b>{Tr(T.Range)}</b>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<b>{T.Network}</b>
|
||||
<b>{Tr(T.Network)}</b>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<b>{T.IcmpType}</b>
|
||||
<b>{Tr(T.IcmpType)}</b>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<b>{T.IcmpTypeV6}</b>
|
||||
<b>{Tr(T.IcmpTypeV6)}</b>
|
||||
</TableCell>
|
||||
<TableCell />
|
||||
</TableRow>
|
||||
@ -181,12 +181,14 @@ const RulesSection = memo(
|
||||
|
||||
return (
|
||||
<TableRow key={index}>
|
||||
<TableCell>{PROTOCOL}</TableCell>
|
||||
<TableCell>{RULE_TYPE}</TableCell>
|
||||
<TableCell>{RANGE}</TableCell>
|
||||
<TableCell>{network}</TableCell>
|
||||
<TableCell>{ICMP_STRING[ICMP_TYPE] || ''}</TableCell>
|
||||
<TableCell>{ICMP_V6_STRING[ICMPv6_TYPE] || ''}</TableCell>
|
||||
<TableCell>{Tr(PROTOCOL)}</TableCell>
|
||||
<TableCell>{Tr(RULE_TYPE)}</TableCell>
|
||||
<TableCell>{Tr(RANGE)}</TableCell>
|
||||
<TableCell>{Tr(network)}</TableCell>
|
||||
<TableCell>{Tr(ICMP_STRING[ICMP_TYPE]) || ''}</TableCell>
|
||||
<TableCell>
|
||||
{Tr(ICMP_V6_STRING[ICMPv6_TYPE]) || ''}
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<IconButton onClick={() => remove(index)}>
|
||||
<DeleteCircledOutline />
|
||||
|
@ -25,11 +25,11 @@ const STRATEGY_TYPES = {
|
||||
|
||||
const VM_SHUTDOWN_TYPES = {
|
||||
terminate: 'Terminate',
|
||||
terminateHard: 'Terminate Hard',
|
||||
terminateHard: 'Terminate hard',
|
||||
}
|
||||
|
||||
const STRATEGY_TYPE = {
|
||||
label: 'Strategy',
|
||||
label: T.Strategy,
|
||||
name: 'ADVANCED.DEPLOYMENT',
|
||||
type: INPUT_TYPES.AUTOCOMPLETE,
|
||||
optionsOnly: true,
|
||||
@ -48,7 +48,7 @@ const STRATEGY_TYPE = {
|
||||
}
|
||||
|
||||
const VM_SHUTDOWN_TYPE = {
|
||||
label: 'VM Shutdown',
|
||||
label: T.VMShutdownAction,
|
||||
name: 'ADVANCED.VMSHUTDOWN',
|
||||
type: INPUT_TYPES.AUTOCOMPLETE,
|
||||
optionsOnly: true,
|
||||
|
@ -22,7 +22,7 @@ import {
|
||||
CUSTOM_ATTRIBUTES_SCHEMA,
|
||||
} from 'client/components/Forms/ServiceTemplate/CreateForm/Steps/Extra/customAttributes/schema'
|
||||
import { FormWithSchema, Legend } from 'client/components/Forms'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
import { Translate, Tr } from 'client/components/HOC'
|
||||
import { DeleteCircledOutline, AddCircledOutline } from 'iconoir-react'
|
||||
import { Stack, FormControl, Divider, Button, Box } from '@mui/material'
|
||||
import List from '@mui/material/List'
|
||||
@ -30,6 +30,7 @@ import ListItem from '@mui/material/ListItem'
|
||||
import ListItemText from '@mui/material/ListItemText'
|
||||
import SubmitButton from 'client/components/FormControl/SubmitButton'
|
||||
import { T } from 'client/constants'
|
||||
import { sentenceCase } from 'client/utils'
|
||||
|
||||
export const SECTION_ID = 'CUSTOM_ATTRIBUTES'
|
||||
|
||||
@ -102,10 +103,13 @@ const CustomAttributesSection = ({ stepId }) => {
|
||||
{customattributes?.map(
|
||||
({ id, name, defaultvalue, description, mandatory, type }, index) => {
|
||||
const secondaryFields = [
|
||||
description && `Description: ${description}`,
|
||||
defaultvalue && `Default value: ${defaultvalue}`,
|
||||
type && `Type: ${type}`,
|
||||
mandatory && `Mandatory: ${mandatory ? 'Yes' : 'No'}`,
|
||||
description && `${Tr(T.Description)}: ${description}`,
|
||||
defaultvalue && `${Tr(T.DefaultValue)}: ${defaultvalue}`,
|
||||
type && `${Tr(T.Type)}: ${Tr(sentenceCase(type))}`,
|
||||
mandatory &&
|
||||
`${Tr(T.Mandatory)}: ${
|
||||
mandatory ? `${Tr(T.Yes)}` : `${Tr(T.No)}`
|
||||
}`,
|
||||
].filter(Boolean)
|
||||
|
||||
return (
|
||||
|
@ -14,7 +14,11 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { string, boolean, number } from 'yup'
|
||||
import { getObjectSchemaFromFields, arrayToOptions } from 'client/utils'
|
||||
import {
|
||||
getObjectSchemaFromFields,
|
||||
arrayToOptions,
|
||||
sentenceCase,
|
||||
} from 'client/utils'
|
||||
import { INPUT_TYPES, T } from 'client/constants'
|
||||
|
||||
const getTypeProp = (type) => {
|
||||
@ -60,10 +64,13 @@ const CA_TYPES = {
|
||||
|
||||
const CA_TYPE = {
|
||||
name: 'type',
|
||||
label: 'Type',
|
||||
label: T.Type,
|
||||
type: INPUT_TYPES.AUTOCOMPLETE,
|
||||
optionsOnly: true,
|
||||
values: arrayToOptions(Object.values(CA_TYPES), { addEmpty: false }),
|
||||
values: arrayToOptions(Object.values(CA_TYPES), {
|
||||
addEmpty: false,
|
||||
getText: (type) => sentenceCase(type),
|
||||
}),
|
||||
|
||||
defaultValueProp: CA_TYPES.text,
|
||||
validation: string()
|
||||
.trim()
|
||||
@ -78,7 +85,7 @@ const CA_TYPE = {
|
||||
|
||||
const NAME = {
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
label: T.Name,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
validation: string()
|
||||
.trim()
|
||||
@ -95,7 +102,7 @@ const NAME = {
|
||||
|
||||
const DESCRIPTION = {
|
||||
name: 'description',
|
||||
label: 'Description',
|
||||
label: T.Description,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
validation: string()
|
||||
.trim()
|
||||
@ -106,7 +113,7 @@ const DESCRIPTION = {
|
||||
|
||||
const DEFAULT_VALUE_TEXT = {
|
||||
name: 'defaultvalue',
|
||||
label: 'Default value',
|
||||
label: T.DefaultValue,
|
||||
dependOf: CA_TYPE.name,
|
||||
|
||||
htmlType: (type) => type === CA_TYPES.password && INPUT_TYPES.HIDDEN,
|
||||
@ -122,7 +129,7 @@ const DEFAULT_VALUE_TEXT = {
|
||||
|
||||
const DEFAULT_VALUE_RANGE_MIN = {
|
||||
name: 'defaultvaluerangemin',
|
||||
label: 'Min range',
|
||||
label: T.MinRange,
|
||||
dependOf: CA_TYPE.name,
|
||||
|
||||
htmlType: (type) =>
|
||||
@ -137,7 +144,7 @@ const DEFAULT_VALUE_RANGE_MIN = {
|
||||
|
||||
const DEFAULT_VALUE_RANGE_MAX = {
|
||||
name: 'defaultvaluerangemax',
|
||||
label: 'Max range',
|
||||
label: T.MaxRange,
|
||||
dependOf: CA_TYPE.name,
|
||||
htmlType: (type) =>
|
||||
![CA_TYPES.range, CA_TYPES.rangefloat].includes(type) && INPUT_TYPES.HIDDEN,
|
||||
@ -151,7 +158,7 @@ const DEFAULT_VALUE_RANGE_MAX = {
|
||||
|
||||
const DEFAULT_VALUE_LIST = {
|
||||
name: 'defaultvaluelist',
|
||||
label: 'Comma separated list of options',
|
||||
label: T.UIOptionsConcept,
|
||||
dependOf: CA_TYPE.name,
|
||||
htmlType: (type) =>
|
||||
![CA_TYPES.listmultiple, CA_TYPES.list].includes(type) &&
|
||||
|
@ -22,7 +22,7 @@ import {
|
||||
NETWORK_INPUT_SCHEMA,
|
||||
} from 'client/components/Forms/ServiceTemplate/CreateForm/Steps/Extra/networking/schema'
|
||||
import { FormWithSchema, Legend } from 'client/components/Forms'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
import { Translate, Tr } from 'client/components/HOC'
|
||||
import { DeleteCircledOutline, AddCircledOutline } from 'iconoir-react'
|
||||
import { Stack, FormControl, Divider, Button, Box } from '@mui/material'
|
||||
import List from '@mui/material/List'
|
||||
@ -30,6 +30,7 @@ import ListItem from '@mui/material/ListItem'
|
||||
import ListItemText from '@mui/material/ListItemText'
|
||||
import SubmitButton from 'client/components/FormControl/SubmitButton'
|
||||
import { T } from 'client/constants'
|
||||
import { sentenceCase } from 'client/utils'
|
||||
|
||||
export const SECTION_ID = 'NETWORKING'
|
||||
|
||||
@ -105,10 +106,10 @@ const NetworkingSection = ({ stepId }) => {
|
||||
{networks?.map(
|
||||
({ name, description, netextra, id, network, type }, index) => {
|
||||
const secondaryFields = [
|
||||
description && `Description: ${description}`,
|
||||
type && `Type: ${type}`,
|
||||
network && `Network: ${network}`,
|
||||
netextra && `Extra: ${netextra}`,
|
||||
description && `${Tr(T.Description)}: ${description}`,
|
||||
type && `${Tr(T.Type)}: ${Tr(sentenceCase(type))}`,
|
||||
network && `${Tr(T.Network)}: ${network}`,
|
||||
netextra && `${Tr(T.Extra)}: ${netextra}`,
|
||||
].filter(Boolean)
|
||||
|
||||
return (
|
||||
|
@ -28,6 +28,7 @@ import {
|
||||
useTheme,
|
||||
} from '@mui/material'
|
||||
import { T } from 'client/constants'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
export const SECTION_ID = 'ADVANCEDPARAMS'
|
||||
|
||||
@ -76,7 +77,7 @@ const AdvancedParametersSection = ({ stepId, roleConfigs, onChange }) => {
|
||||
filter: 'brightness(90%)',
|
||||
}}
|
||||
>
|
||||
<Typography variant="body1">{T.AdvancedParams}</Typography>
|
||||
<Typography variant="body1">{Tr(T.AdvancedParams)}</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Box
|
||||
|
@ -15,7 +15,7 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { object, string } from 'yup'
|
||||
import { getValidationFromFields, arrayToOptions } from 'client/utils'
|
||||
import { INPUT_TYPES } from 'client/constants'
|
||||
import { INPUT_TYPES, T } from 'client/constants'
|
||||
import { SECTION_ID as ADVANCED_SECTION_ID } from 'client/components/Forms/ServiceTemplate/CreateForm/Steps/RoleConfig/AdvancedParameters'
|
||||
|
||||
const SHUTDOWN_TYPES = {
|
||||
@ -31,7 +31,7 @@ const SHUTDOWN_ENUMS_ONEFLOW = {
|
||||
|
||||
const SHUTDOWN_TYPE = {
|
||||
name: `${ADVANCED_SECTION_ID}.SHUTDOWNTYPE`,
|
||||
label: 'VM Shutdown action',
|
||||
label: T.VMShutdownAction,
|
||||
type: INPUT_TYPES.AUTOCOMPLETE,
|
||||
optionsOnly: true,
|
||||
values: arrayToOptions(Object.keys(SHUTDOWN_TYPES), {
|
||||
|
@ -28,7 +28,7 @@ import {
|
||||
ELASTICITY_TYPES,
|
||||
} from 'client/components/Forms/ServiceTemplate/CreateForm/Steps/RoleConfig/ElasticityPolicies/schema'
|
||||
import { FormWithSchema } from 'client/components/Forms'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
import { Translate, Tr } from 'client/components/HOC'
|
||||
import { DeleteCircledOutline, AddCircledOutline } from 'iconoir-react'
|
||||
import {
|
||||
Accordion,
|
||||
@ -102,7 +102,7 @@ const ElasticityPoliciesSection = ({ stepId, selectedRoleIndex }) => {
|
||||
filter: 'brightness(90%)',
|
||||
}}
|
||||
>
|
||||
<Typography variant="body1">{T.ElasticityPolicies}</Typography>
|
||||
<Typography variant="body1">{Tr(T.ElasticityPolicies)}</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<FormProvider {...methods}>
|
||||
@ -149,14 +149,14 @@ const ElasticityPoliciesSection = ({ stepId, selectedRoleIndex }) => {
|
||||
index
|
||||
) => {
|
||||
const secondaryFields = [
|
||||
`Expression: ${EXPRESSION}`,
|
||||
`Adjust: ${ADJUST}`,
|
||||
`Cooldown: ${COOLDOWN}`,
|
||||
`Period: ${PERIOD}`,
|
||||
`${Tr(T.Expression)}: ${EXPRESSION}`,
|
||||
`${Tr(T.Adjust)}: ${ADJUST}`,
|
||||
`${Tr(T.Cooldown)}: ${COOLDOWN}`,
|
||||
`${Tr(T.Period)}: ${PERIOD}`,
|
||||
`#: ${PERIOD_NUMBER}`,
|
||||
]
|
||||
if (MIN !== undefined && TYPE === 'PERCENTAGE_CHANGE') {
|
||||
secondaryFields.push(`Min: ${MIN}`)
|
||||
secondaryFields.push(`${Tr(T.Min)}: ${MIN}`)
|
||||
}
|
||||
|
||||
return (
|
||||
@ -178,7 +178,7 @@ const ElasticityPoliciesSection = ({ stepId, selectedRoleIndex }) => {
|
||||
sx={{ '&:hover': { bgcolor: 'action.hover' } }}
|
||||
>
|
||||
<ListItemText
|
||||
primary={ELASTICITY_TYPES?.[TYPE]}
|
||||
primary={Tr(ELASTICITY_TYPES?.[TYPE])}
|
||||
primaryTypographyProps={{ variant: 'body1' }}
|
||||
secondary={secondaryFields.join(' | ')}
|
||||
/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { object, string, number } from 'yup'
|
||||
import { getValidationFromFields, arrayToOptions } from 'client/utils'
|
||||
import { INPUT_TYPES } from 'client/constants'
|
||||
import { INPUT_TYPES, T } from 'client/constants'
|
||||
|
||||
// Define the CA types
|
||||
export const ELASTICITY_TYPES = {
|
||||
@ -37,7 +37,7 @@ export const createElasticityPolicyFields = (pathPrefix) => {
|
||||
return [
|
||||
{
|
||||
name: getPath('TYPE'),
|
||||
label: 'Type',
|
||||
label: T.Type,
|
||||
type: INPUT_TYPES.AUTOCOMPLETE,
|
||||
optionsOnly: true,
|
||||
cy: 'roleconfig-elasticitypolicies',
|
||||
@ -55,7 +55,7 @@ export const createElasticityPolicyFields = (pathPrefix) => {
|
||||
},
|
||||
{
|
||||
name: getPath('ADJUST'),
|
||||
label: 'Adjust',
|
||||
label: T.Adjust,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
cy: 'roleconfig-elasticitypolicies',
|
||||
fieldProps: {
|
||||
@ -66,7 +66,7 @@ export const createElasticityPolicyFields = (pathPrefix) => {
|
||||
},
|
||||
{
|
||||
name: getPath('MIN'),
|
||||
label: 'Min',
|
||||
label: T.Min,
|
||||
dependOf: getPath('TYPE'),
|
||||
htmlType: (type) =>
|
||||
// ONLY DISPLAY ON PERCENTAGE_CHANGE
|
||||
@ -86,7 +86,7 @@ export const createElasticityPolicyFields = (pathPrefix) => {
|
||||
{
|
||||
name: getPath('EXPRESSION'),
|
||||
dependOf: getPath('TYPE'),
|
||||
label: 'Expression',
|
||||
label: T.Expression,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
cy: 'roleconfig-elasticitypolicies',
|
||||
validation: string().trim().required(),
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
import { object, number } from 'yup'
|
||||
import { getValidationFromFields } from 'client/utils'
|
||||
import { INPUT_TYPES } from 'client/constants'
|
||||
import { INPUT_TYPES, T } from 'client/constants'
|
||||
|
||||
const MAX_VALUE = 999999
|
||||
|
||||
@ -34,7 +34,7 @@ export const createMinMaxVmsFields = (pathPrefix, cardinality) => {
|
||||
return [
|
||||
{
|
||||
name: getPath('min_vms'),
|
||||
label: 'Min VMs',
|
||||
label: T.RolesMinVms,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
cy: 'elasticity',
|
||||
validation: number()
|
||||
@ -51,7 +51,7 @@ export const createMinMaxVmsFields = (pathPrefix, cardinality) => {
|
||||
},
|
||||
{
|
||||
name: getPath('max_vms'),
|
||||
label: 'Max VMs',
|
||||
label: T.RolesMaxVms,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
cy: 'elasticity',
|
||||
validation: number()
|
||||
@ -66,7 +66,7 @@ export const createMinMaxVmsFields = (pathPrefix, cardinality) => {
|
||||
},
|
||||
{
|
||||
name: getPath('cooldown'),
|
||||
label: 'Cooldown',
|
||||
label: T.Cooldown,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
cy: 'elasticity',
|
||||
validation: number()
|
||||
|
@ -28,7 +28,7 @@ import {
|
||||
SCHED_TYPES,
|
||||
} from 'client/components/Forms/ServiceTemplate/CreateForm/Steps/RoleConfig/ScheduledPolicies/schema'
|
||||
import { FormWithSchema } from 'client/components/Forms'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
import { Translate, Tr } from 'client/components/HOC'
|
||||
import { DeleteCircledOutline, AddCircledOutline } from 'iconoir-react'
|
||||
import {
|
||||
Accordion,
|
||||
@ -102,7 +102,7 @@ const ScheduledPoliciesSection = ({ stepId, selectedRoleIndex }) => {
|
||||
filter: 'brightness(90%)',
|
||||
}}
|
||||
>
|
||||
<Typography variant="body1">{T.ScheduledPolicies}</Typography>
|
||||
<Typography variant="body1">{Tr(T.ScheduledPolicies)}</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<FormProvider {...methods}>
|
||||
@ -139,14 +139,16 @@ const ScheduledPoliciesSection = ({ stepId, selectedRoleIndex }) => {
|
||||
{ TIMEFORMAT, SCHEDTYPE, ADJUST, MIN, TIMEEXPRESSION },
|
||||
index
|
||||
) => {
|
||||
const timeformatTrans = Tr(TIMEFORMAT)
|
||||
|
||||
const secondaryFields = [
|
||||
`Time Expression: ${TIMEEXPRESSION}`,
|
||||
`Adjust: ${ADJUST}`,
|
||||
`Time Format: ${TIMEFORMAT}`,
|
||||
`${Tr(T.TimeExpression)}: ${TIMEEXPRESSION}`,
|
||||
`${Tr(T.Adjust)}: ${ADJUST}`,
|
||||
`${Tr(T.TimeFormat)}: ${timeformatTrans}`,
|
||||
]
|
||||
|
||||
if (MIN !== undefined) {
|
||||
secondaryFields?.push(`Min: ${MIN}`)
|
||||
secondaryFields?.push(`${Tr(T.Min)}: ${MIN}`)
|
||||
}
|
||||
|
||||
return (
|
||||
@ -168,7 +170,7 @@ const ScheduledPoliciesSection = ({ stepId, selectedRoleIndex }) => {
|
||||
sx={{ '&:hover': { bgcolor: 'action.hover' } }}
|
||||
>
|
||||
<ListItemText
|
||||
primary={SCHED_TYPES?.[SCHEDTYPE]}
|
||||
primary={Tr(SCHED_TYPES?.[SCHEDTYPE])}
|
||||
primaryTypographyProps={{ variant: 'body1' }}
|
||||
secondary={secondaryFields.join(' | ')}
|
||||
/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { object, string, number } from 'yup'
|
||||
import { getValidationFromFields, arrayToOptions } from 'client/utils'
|
||||
import { INPUT_TYPES } from 'client/constants'
|
||||
import { INPUT_TYPES, T } from 'client/constants'
|
||||
|
||||
const TIME_TYPES = {
|
||||
none: '',
|
||||
@ -49,7 +49,7 @@ export const createScheduledPolicyFields = (pathPrefix) => {
|
||||
return [
|
||||
{
|
||||
name: getPath('SCHEDTYPE'),
|
||||
label: 'Type',
|
||||
label: T.Type,
|
||||
type: INPUT_TYPES.AUTOCOMPLETE,
|
||||
optionsOnly: true,
|
||||
cy: 'roleconfig-scheduledpolicies',
|
||||
@ -66,7 +66,7 @@ export const createScheduledPolicyFields = (pathPrefix) => {
|
||||
},
|
||||
{
|
||||
name: getPath('ADJUST'),
|
||||
label: 'Adjust',
|
||||
label: T.Adjust,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
cy: 'roleconfig-scheduledpolicies',
|
||||
validation: string()
|
||||
@ -77,7 +77,7 @@ export const createScheduledPolicyFields = (pathPrefix) => {
|
||||
},
|
||||
{
|
||||
name: getPath('MIN'),
|
||||
label: 'Min',
|
||||
label: T.Min,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
cy: 'roleconfig-scheduledpolicies',
|
||||
fieldProps: {
|
||||
@ -88,7 +88,7 @@ export const createScheduledPolicyFields = (pathPrefix) => {
|
||||
},
|
||||
{
|
||||
name: getPath('TIMEFORMAT'),
|
||||
label: 'Time Format',
|
||||
label: T.TimeFormat,
|
||||
type: INPUT_TYPES.AUTOCOMPLETE,
|
||||
optionsOnly: true,
|
||||
cy: 'roleconfig-scheduledpolicies',
|
||||
@ -102,7 +102,7 @@ export const createScheduledPolicyFields = (pathPrefix) => {
|
||||
},
|
||||
{
|
||||
name: getPath('TIMEEXPRESSION'),
|
||||
label: 'Time Expression',
|
||||
label: T.TimeExpression,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
cy: 'roleconfig-scheduledpolicies',
|
||||
validation: string()
|
||||
|
@ -21,6 +21,7 @@ import { DataGrid } from '@mui/x-data-grid'
|
||||
import makeStyles from '@mui/styles/makeStyles'
|
||||
import { Box, Checkbox, TextField, Autocomplete } from '@mui/material'
|
||||
import { T } from 'client/constants'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { Legend } from 'client/components/Forms'
|
||||
import { STEP_ID as EXTRA_ID } from 'client/components/Forms/ServiceTemplate/CreateForm/Steps/Extra'
|
||||
import _ from 'lodash'
|
||||
@ -193,7 +194,7 @@ const RoleNetwork = ({ stepId, selectedRoleIndex }) => {
|
||||
field: 'select',
|
||||
disableColumnMenu: true,
|
||||
sortable: false,
|
||||
headerName: 'Select',
|
||||
headerName: Tr(T.Select),
|
||||
width: 100,
|
||||
renderCell: (params) => (
|
||||
<Checkbox
|
||||
@ -209,14 +210,14 @@ const RoleNetwork = ({ stepId, selectedRoleIndex }) => {
|
||||
field: 'network',
|
||||
disableColumnMenu: true,
|
||||
flex: 1,
|
||||
headerName: 'Network',
|
||||
headerName: Tr(T.Network),
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
field: 'aliasToggle',
|
||||
disableColumnMenu: true,
|
||||
sortable: false,
|
||||
headerName: 'NIC Alias',
|
||||
headerName: Tr(T.NICAlias),
|
||||
width: 110,
|
||||
renderCell: (params) =>
|
||||
params?.row?.rowSelected && (
|
||||
@ -233,7 +234,7 @@ const RoleNetwork = ({ stepId, selectedRoleIndex }) => {
|
||||
field: 'alias',
|
||||
disableColumnMenu: true,
|
||||
flex: 1,
|
||||
headerName: 'Alias',
|
||||
headerName: Tr(T.Alias),
|
||||
width: 200,
|
||||
renderCell: (params) =>
|
||||
params?.row?.aliasSelected && (
|
||||
@ -281,7 +282,12 @@ const RoleNetwork = ({ stepId, selectedRoleIndex }) => {
|
||||
className={classes.root}
|
||||
rows={fields}
|
||||
columns={columns}
|
||||
localeText={{ noRowsLabel: 'No networks have been defined' }}
|
||||
localeText={{
|
||||
noRowsLabel: 'No networks have been defined',
|
||||
MuiTablePagination: {
|
||||
labelRowsPerPage: Tr(T.RowsPerPage),
|
||||
},
|
||||
}}
|
||||
autoHeight
|
||||
rowsPerPageOptions={[5, 10, 25, 50, 100]}
|
||||
disableSelectionOnClick
|
||||
|
@ -22,6 +22,7 @@ import RoleVmVmPanel from 'client/components/Forms/ServiceTemplate/CreateForm/St
|
||||
import RoleColumn from 'client/components/Forms/ServiceTemplate/CreateForm/Steps/Roles/rolesColumn'
|
||||
import VmTemplatesPanel from 'client/components/Forms/ServiceTemplate/CreateForm/Steps/Roles/vmTemplatesPanel'
|
||||
import RoleSummary from 'client/components/Forms/ServiceTemplate/CreateForm/Steps/Roles/roleSummary'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
export const STEP_ID = 'roledefinition'
|
||||
|
||||
@ -118,7 +119,7 @@ const Content = () => {
|
||||
*/
|
||||
const RoleDefinition = () => ({
|
||||
id: STEP_ID,
|
||||
label: 'Role Definition',
|
||||
label: T.RoleDefinition,
|
||||
resolver: SCHEMA,
|
||||
optionsValidate: { abortEarly: false },
|
||||
content: Content,
|
||||
|
@ -22,6 +22,7 @@ import {
|
||||
} from '@mui/material'
|
||||
import PropTypes from 'prop-types'
|
||||
import { T } from 'client/constants'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { Component } from 'react'
|
||||
/**
|
||||
* RoleSummary displays detailed information about a VM role, including its configuration and affinity settings.
|
||||
@ -51,7 +52,7 @@ const RoleSummary = ({ role, selectedRoleIndex }) => (
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6" component="div" gutterBottom>
|
||||
#{selectedRoleIndex + 1 ?? 0} Role Configuration
|
||||
#{selectedRoleIndex + 1 ?? 0} {Tr(T.RoleConfiguration)}
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
@ -59,7 +60,7 @@ const RoleSummary = ({ role, selectedRoleIndex }) => (
|
||||
color={role?.NAME ? 'text.primary' : 'text.disabled'}
|
||||
gutterBottom
|
||||
>
|
||||
Name: {role?.NAME || 'Enter a name for this role.'}
|
||||
{Tr(T.Name)}: {role?.NAME || Tr(T.RoleEnterName)}
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
@ -73,7 +74,7 @@ const RoleSummary = ({ role, selectedRoleIndex }) => (
|
||||
}
|
||||
gutterBottom
|
||||
>
|
||||
{T.NumberOfVms}: {role?.CARDINALITY}
|
||||
{Tr(T.NumberOfVms)}: {role?.CARDINALITY}
|
||||
</Typography>
|
||||
|
||||
{role?.SELECTED_VM_TEMPLATE_ID ? (
|
||||
@ -89,12 +90,12 @@ const RoleSummary = ({ role, selectedRoleIndex }) => (
|
||||
}
|
||||
gutterBottom
|
||||
>
|
||||
VM Template ID: {role?.SELECTED_VM_TEMPLATE_ID}
|
||||
{Tr(T.VMTemplate)} {Tr(T.ID)}: {role?.SELECTED_VM_TEMPLATE_ID}
|
||||
</Typography>
|
||||
</>
|
||||
) : (
|
||||
<Typography variant="body2" color="text.disabled" gutterBottom>
|
||||
Select a VM template.
|
||||
{Tr(T.SelectVmTemplate)}
|
||||
</Typography>
|
||||
)}
|
||||
<Divider />
|
||||
@ -103,11 +104,11 @@ const RoleSummary = ({ role, selectedRoleIndex }) => (
|
||||
color={role?.NETWORKS ? 'text.primary' : 'text.disabled'}
|
||||
gutterBottom
|
||||
>
|
||||
Networks: {role?.NETWORKS || 'Select a network for this role.'}
|
||||
{Tr(T.Networks)}: {role?.NETWORKS || ' ' + Tr(T.RoleSelectNetwork)}
|
||||
</Typography>
|
||||
|
||||
<Typography color={'text.primary'} sx={{ fontSize: 16 }} gutterBottom>
|
||||
Role Elasticity
|
||||
{Tr(T.RoleElasticity)}
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
@ -115,8 +116,7 @@ const RoleSummary = ({ role, selectedRoleIndex }) => (
|
||||
color={role?.MINVMS ? 'text.primary' : 'text.disabled'}
|
||||
gutterBottom
|
||||
>
|
||||
Min VMs:
|
||||
{role?.MINVMS || ' Minimum number of VMs for elasticity adjustments.'}
|
||||
{Tr(T.RolesMinVms)}:{role?.MINVMS || ' ' + Tr(T.RoleMinElasticity)}
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
@ -124,8 +124,7 @@ const RoleSummary = ({ role, selectedRoleIndex }) => (
|
||||
color={role?.MAXVMS ? 'text.primary' : 'text.disabled'}
|
||||
gutterBottom
|
||||
>
|
||||
Max VMs:
|
||||
{role?.MAXVMS || ' Maximum number of VMs for elasticity adjustments.'}
|
||||
{Tr(T.RolesMaxVms)}:{role?.MAXVMS || ' ' + Tr(T.RoleMaxElasticity)}
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
@ -133,9 +132,7 @@ const RoleSummary = ({ role, selectedRoleIndex }) => (
|
||||
color={role?.MAXVMS ? 'text.primary' : 'text.disabled'}
|
||||
gutterBottom
|
||||
>
|
||||
Cooldown:
|
||||
{role?.COOLDOWN ||
|
||||
' Duration after a scale operation in seconds. If it is not set, the default set in oneflow-server.conf will be used.'}
|
||||
{Tr(T.Cooldown)}:{role?.COOLDOWN || ' ' + Tr(T.RoleDurationScale)}
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
@ -143,7 +140,7 @@ const RoleSummary = ({ role, selectedRoleIndex }) => (
|
||||
sx={{ fontSize: 14 }}
|
||||
gutterBottom
|
||||
>
|
||||
Elasticity Policies
|
||||
{Tr(T.ElasticityPolicies)}
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
@ -153,8 +150,8 @@ const RoleSummary = ({ role, selectedRoleIndex }) => (
|
||||
}
|
||||
gutterBottom
|
||||
>
|
||||
Type:
|
||||
{role?.ELASTICITYPOLICIES?.TYPE || ' Adjustment type'}
|
||||
{Tr(T.Type)}:
|
||||
{role?.ELASTICITYPOLICIES?.TYPE || ' ' + Tr(T.RoleAdjustmentType)}
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
@ -164,8 +161,9 @@ const RoleSummary = ({ role, selectedRoleIndex }) => (
|
||||
}
|
||||
gutterBottom
|
||||
>
|
||||
Adjust:
|
||||
{role?.ELASTICITYPOLICIES?.ADJUST || ' Positive or negative adjustment'}
|
||||
{Tr(T.Adjust)}:
|
||||
{role?.ELASTICITYPOLICIES?.ADJUST ||
|
||||
' ' + Tr(T.RoleAdjustmentTypePositiveNegative)}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
<CardActions sx={{ p: 2, pt: 0 }}>
|
||||
@ -174,11 +172,11 @@ const RoleSummary = ({ role, selectedRoleIndex }) => (
|
||||
color="textSecondary"
|
||||
sx={{ opacity: 0.7 }}
|
||||
>
|
||||
<strong>VM Group Configuration:</strong>
|
||||
<strong>{Tr(T.VMGroupConfiguration)}:</strong>
|
||||
<ul>
|
||||
<li>Define roles and placement constraints.</li>
|
||||
<li>Optimize performance and fault tolerance.</li>
|
||||
<li>Manage multi-VM applications efficiently.</li>
|
||||
<li>{Tr(T.RoleDefineRoles)}</li>
|
||||
<li>{Tr(T.RoleOptimize)}</li>
|
||||
<li>{Tr(T.RoleManageApps)}</li>
|
||||
</ul>
|
||||
</Typography>
|
||||
</CardActions>
|
||||
|
@ -17,6 +17,8 @@ import { useCallback, Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Box, Button, List, ListItem, IconButton } from '@mui/material'
|
||||
import { Cancel } from 'iconoir-react'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
/**
|
||||
* RoleColumn component for displaying and managing roles.
|
||||
@ -84,7 +86,7 @@ const RoleColumn = ({
|
||||
size="large"
|
||||
data-cy="add-role"
|
||||
>
|
||||
Add Role
|
||||
{Tr(T.AddRole)}
|
||||
</Button>
|
||||
)}
|
||||
<Box
|
||||
|
@ -17,6 +17,7 @@ import { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Box, TextField, Typography } from '@mui/material'
|
||||
import { T } from 'client/constants'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
/**
|
||||
* Role Panel component for managing roles.
|
||||
@ -45,10 +46,10 @@ const RoleVmVmPanel = ({ roles, onChange, selectedRoleIndex }) => {
|
||||
return (
|
||||
<Box p={2}>
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<Typography variant="h6">Role Details</Typography>
|
||||
<Typography variant="h6">{Tr(T.RoleDetails)}</Typography>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<TextField
|
||||
label="Role Name"
|
||||
label={Tr(T.RoleName)}
|
||||
name="NAME"
|
||||
value={roles?.[selectedRoleIndex]?.NAME ?? ''}
|
||||
onChange={handleInputChange}
|
||||
@ -61,7 +62,7 @@ const RoleVmVmPanel = ({ roles, onChange, selectedRoleIndex }) => {
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<TextField
|
||||
type="number"
|
||||
label={T.NumberOfVms}
|
||||
label={Tr(T.NumberOfVms)}
|
||||
value={roles?.[selectedRoleIndex]?.CARDINALITY ?? 1}
|
||||
onChange={handleInputChange}
|
||||
name="CARDINALITY"
|
||||
|
@ -31,6 +31,8 @@ import {
|
||||
import { useLazyGetTemplatesQuery } from 'client/features/OneApi/vmTemplate'
|
||||
import { useGeneralApi } from 'client/features/General'
|
||||
import { DateTime } from 'luxon'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
const convertTimestampToDate = (timestamp) =>
|
||||
DateTime.fromSeconds(parseInt(timestamp)).toFormat('dd/MM/yyyy HH:mm:ss')
|
||||
@ -57,9 +59,7 @@ const VmTemplatesPanel = ({ roles, selectedRoleIndex, onChange }) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
enqueueError(
|
||||
`Error fetching VM templates data: ${error?.message ?? error}`
|
||||
)
|
||||
enqueueError(T.ErrorVmTemplateFetching, error?.message ?? error)
|
||||
}
|
||||
}, [error, enqueueError])
|
||||
|
||||
@ -89,18 +89,18 @@ const VmTemplatesPanel = ({ roles, selectedRoleIndex, onChange }) => {
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
VM Templates
|
||||
{Tr(T.VMTemplates)}
|
||||
</Typography>
|
||||
<Paper sx={{ overflow: 'auto', marginBottom: 2 }}>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell padding="checkbox"></TableCell>
|
||||
<TableCell>ID</TableCell>
|
||||
<TableCell>Name</TableCell>
|
||||
<TableCell>Owner</TableCell>
|
||||
<TableCell>Group</TableCell>
|
||||
<TableCell>Registration time</TableCell>
|
||||
<TableCell>{Tr(T.ID)}</TableCell>
|
||||
<TableCell>{Tr(T.Name)}</TableCell>
|
||||
<TableCell>{Tr(T.Owner)}</TableCell>
|
||||
<TableCell>{Tr(T.Group)}</TableCell>
|
||||
<TableCell>{Tr(T.RegistrationTime)}</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
@ -156,6 +156,7 @@ const VmTemplatesPanel = ({ roles, selectedRoleIndex, onChange }) => {
|
||||
page={page}
|
||||
onPageChange={handleChangePage}
|
||||
onRowsPerPageChange={handleChangeRowsPerPage}
|
||||
labelRowsPerPage={Tr(T.RowsPerPage)}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
|
@ -27,6 +27,7 @@ import {
|
||||
|
||||
import PropTypes from 'prop-types'
|
||||
import { T } from 'client/constants'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { Legend } from 'client/components/Forms'
|
||||
|
||||
import { mapNameByIndex } from 'client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/schema'
|
||||
@ -77,7 +78,7 @@ const ScheduleActionsSection = ({ oneConfig, adminGroup }) => {
|
||||
|
||||
return (
|
||||
<Box mt={2}>
|
||||
<Legend title={'Add Charters Values Configuration'} />
|
||||
<Legend title={Tr(T.AddChartes)} />
|
||||
<Box sx={{ width: '100%', gridColumn: '1 / -1' }}>
|
||||
<Stack flexDirection="row" gap="1em">
|
||||
<CreateSchedButton
|
||||
|
@ -13,14 +13,14 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { INPUT_TYPES } from 'client/constants'
|
||||
import { INPUT_TYPES, T } from 'client/constants'
|
||||
import { Field, getObjectSchemaFromFields } from 'client/utils'
|
||||
import { string, number } from 'yup'
|
||||
|
||||
/** @type {Field} Name field */
|
||||
const NAME_FIELD = {
|
||||
name: 'NAME',
|
||||
label: 'Service Name',
|
||||
label: T.ServiceName,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
validation: string()
|
||||
.trim()
|
||||
@ -34,7 +34,7 @@ const NAME_FIELD = {
|
||||
/** @type {Field} Description field */
|
||||
const INSTANCE_FIELD = {
|
||||
name: 'INSTANCES',
|
||||
label: 'Number of instances',
|
||||
label: T.NumberOfInstances,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
validation: number().required(),
|
||||
fieldProps: {
|
||||
|
@ -25,6 +25,9 @@ import {
|
||||
TerminalSimple as SshIcon,
|
||||
} from 'iconoir-react'
|
||||
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
/**
|
||||
* Renders a NIC card with details and actions.
|
||||
*
|
||||
@ -113,7 +116,7 @@ const NicCard = ({ info = {}, removeNic, selectNic, active } = {}) => {
|
||||
>
|
||||
<Tooltip title={`#${nicId?.slice(0, 10)}`} arrow>
|
||||
<Typography noWrap variant="body1">
|
||||
{autonetworkselect ? 'Automatically selected' : network}
|
||||
{autonetworkselect ? Tr(T['nic.card.automatic']) : network}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
|
||||
@ -125,19 +128,19 @@ const NicCard = ({ info = {}, removeNic, selectNic, active } = {}) => {
|
||||
}}
|
||||
>
|
||||
{management && (
|
||||
<Tooltip title="Management interface" arrow>
|
||||
<Tooltip title={Tr(T['nic.card.management'])} arrow>
|
||||
<ManagementIcon height={18} width={18} />
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{rdpconnection && (
|
||||
<Tooltip title="RDP Connection" arrow>
|
||||
<Tooltip title={Tr(T.RdpConnection)} arrow>
|
||||
<RdpIcon height={18} width={18} />
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{sshconnection && (
|
||||
<Tooltip title="SSH Connection" arrow>
|
||||
<Tooltip title={Tr(T.SshConnection)} arrow>
|
||||
<SshIcon height={18} width={18} />
|
||||
</Tooltip>
|
||||
)}
|
||||
@ -202,7 +205,7 @@ const NicCard = ({ info = {}, removeNic, selectNic, active } = {}) => {
|
||||
}}
|
||||
>
|
||||
<Typography noWrap variant="body2">
|
||||
{secgroup || 'Security Group'}
|
||||
{secgroup || Tr(T.SecurityGroup)}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
|
@ -21,6 +21,8 @@ import Legend from 'client/components/Forms/Legend'
|
||||
import NicCard from './NicCard'
|
||||
import { useGetVNetworksQuery } from 'client/features/OneApi/network'
|
||||
import { useGetSecGroupsQuery } from 'client/features/OneApi/securityGroup'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
/**
|
||||
* Renders a column of NICs with actions to add, remove, and select NICs.
|
||||
@ -45,7 +47,7 @@ const NicColumn = ({ nics, addNic, removeNic, selectNic, activeNic } = {}) => {
|
||||
height="100%"
|
||||
position="relative"
|
||||
>
|
||||
<Legend title="Configured NIC's" />
|
||||
<Legend title={Tr(T.VirtualRouterNICConfigured)} />
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
@ -75,7 +77,7 @@ const NicColumn = ({ nics, addNic, removeNic, selectNic, activeNic } = {}) => {
|
||||
(vnet) => vnet?.ID === nic?.network_id
|
||||
)?.NAME,
|
||||
}
|
||||
: { network: 'Network Name' }),
|
||||
: { network: Tr(T.VirtualRouterNICNetworkName) }),
|
||||
|
||||
...(nic?.secgroup !== ''
|
||||
? {
|
||||
@ -83,7 +85,7 @@ const NicColumn = ({ nics, addNic, removeNic, selectNic, activeNic } = {}) => {
|
||||
(secgroup) => secgroup?.ID === nic?.secgroup
|
||||
)?.NAME,
|
||||
}
|
||||
: { secgroup: 'Security Group' }),
|
||||
: { secgroup: 'sadfasd' }),
|
||||
}}
|
||||
removeNic={removeNic}
|
||||
selectNic={selectNic}
|
||||
@ -104,7 +106,7 @@ const NicColumn = ({ nics, addNic, removeNic, selectNic, activeNic } = {}) => {
|
||||
marginTop: 'auto',
|
||||
}}
|
||||
>
|
||||
Add NIC
|
||||
{Tr(T.VirtualRouterNICAdd)}
|
||||
</Button>
|
||||
</Box>
|
||||
)
|
||||
|
@ -37,14 +37,14 @@ const NETWORK = {
|
||||
|
||||
const RDP = {
|
||||
name: 'rdpconnection',
|
||||
label: 'Enable RDP',
|
||||
label: T.RdpConnection,
|
||||
type: INPUT_TYPES.SWITCH,
|
||||
validation: boolean().default(() => false),
|
||||
grid: { md: 6 },
|
||||
}
|
||||
const SSH = {
|
||||
name: 'sshconnection',
|
||||
label: 'Enable SSH',
|
||||
label: T.SshConnection,
|
||||
type: INPUT_TYPES.SWITCH,
|
||||
validation: boolean().default(() => false),
|
||||
grid: { md: 6 },
|
||||
@ -52,7 +52,7 @@ const SSH = {
|
||||
|
||||
const FORCEIPV4 = {
|
||||
name: 'IP',
|
||||
label: 'Force IPv4',
|
||||
label: T.VirtualRouterNICForceIpv4,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
validation: number()
|
||||
.min(7) // Shortest possible IPv4
|
||||
@ -63,7 +63,7 @@ const FORCEIPV4 = {
|
||||
|
||||
const FLOATINGIP = {
|
||||
name: 'floating_ip',
|
||||
label: 'Floating IP',
|
||||
label: T.VirtualRouterNICFloatingIP,
|
||||
type: INPUT_TYPES.CHECKBOX,
|
||||
validation: boolean().yesOrNo(),
|
||||
grid: { md: 6 },
|
||||
@ -71,7 +71,7 @@ const FLOATINGIP = {
|
||||
|
||||
const FORCEIPV6 = {
|
||||
name: 'IP6',
|
||||
label: 'Force IPv6',
|
||||
label: T.VirtualRouterNICForceIpv6,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
validation: number()
|
||||
.min(7)
|
||||
@ -82,7 +82,7 @@ const FORCEIPV6 = {
|
||||
|
||||
const MANAGEMENINTERFACE = {
|
||||
name: 'VROUTER_MANAGEMENT',
|
||||
label: 'Management interface',
|
||||
label: T['nic.card.management'],
|
||||
type: INPUT_TYPES.CHECKBOX,
|
||||
validation: boolean().yesOrNo(),
|
||||
grid: { md: 6 },
|
||||
|
@ -20,6 +20,7 @@ import {
|
||||
SCHEMA,
|
||||
} from 'client/components/Forms/VRTemplate/InstantiateForm/Steps/Networking/NicMenu/informationSchema'
|
||||
import { T } from 'client/constants'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { Box, Typography } from '@mui/material'
|
||||
import { useState, useMemo } from 'react'
|
||||
import { useFieldArray, useFormContext, useWatch } from 'react-hook-form'
|
||||
@ -79,25 +80,18 @@ const Content = () => {
|
||||
gutterBottom
|
||||
style={{ color: '#1976d', fontWeight: 'bold' }}
|
||||
>
|
||||
Add a NIC to Start Configuring
|
||||
{Tr(T.VirtualRouterNICStart)}
|
||||
</Typography>
|
||||
<Typography variant="body1" paragraph style={{ marginTop: '16px' }}>
|
||||
In a Virtual Router, a Network Interface Card (NIC) is crucial for
|
||||
managing network traffic efficiently. By adding a NIC, you unlock
|
||||
the ability to create virtual networks and route traffic between
|
||||
virtual machines, ensuring secure and efficient communication within
|
||||
your cloud environment.
|
||||
{Tr(T.VirtualRouterNICStart1)}
|
||||
</Typography>
|
||||
<Typography variant="body1" paragraph style={{ marginTop: '16px' }}>
|
||||
Just add a NIC to your configuration, and you'll gain access to
|
||||
configure network settings for your virtual routers. This includes
|
||||
setting up IP addresses, Security Groups, and more, making your
|
||||
cloud environment more manageable and secure.
|
||||
{Tr(T.VirtualRouterNICStart2)}
|
||||
</Typography>
|
||||
</Box>
|
||||
) : (
|
||||
<FormWithSchema
|
||||
legend={'Network configuration'}
|
||||
legend={Tr(T.VirtualRouterNICNetworkConfiguration)}
|
||||
key={`${STEP_ID}-NIC-${activeNic}`}
|
||||
cy={STEP_ID}
|
||||
fields={FIELDS}
|
||||
|
@ -31,7 +31,7 @@ const TEMPLATEID = {
|
||||
preserveState: true,
|
||||
},
|
||||
validation: mixed()
|
||||
.required('VR template ID missing or malformed!')
|
||||
.required()
|
||||
.default(() => null),
|
||||
grid: { md: 12 },
|
||||
}
|
||||
|
@ -27,6 +27,9 @@ import {
|
||||
import { Trash as ClearIcon } from 'iconoir-react'
|
||||
import SubNode from './SubNode'
|
||||
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
/**
|
||||
* @param {object} root0 - Params
|
||||
* @param {Array} root0.userInputs - Array of user inputs
|
||||
@ -62,7 +65,7 @@ const NodeMenu = ({ userInputs }) => {
|
||||
<TextField
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
placeholder="Search..."
|
||||
placeholder={`${Tr(T.Search)}...`}
|
||||
value={searchTerm}
|
||||
onChange={handleSearchChange}
|
||||
sx={{ paddingBottom: '6px' }}
|
||||
@ -87,7 +90,7 @@ const NodeMenu = ({ userInputs }) => {
|
||||
}}
|
||||
/>
|
||||
}
|
||||
label="Show mandatory only"
|
||||
label={Tr(T.VirtualRouterUserInputsShowMandatory)}
|
||||
/>
|
||||
</FormGroup>
|
||||
{filteredUserInputs.map((userInput, index) => (
|
||||
|
@ -19,6 +19,8 @@ import { Component, useState } from 'react'
|
||||
import UserInputDialog from './UserInputDialog'
|
||||
import { Typography, Box, useTheme, Grid } from '@mui/material'
|
||||
import { WarningCircledOutline as MandatoryIcon } from 'iconoir-react'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
/**
|
||||
* @param {object} root0 - Params
|
||||
@ -30,7 +32,12 @@ const SubNode = ({ userInput }) => {
|
||||
const theme = useTheme()
|
||||
const [dialogOpen, setDialogOpen] = useState(false)
|
||||
const { getValues } = useFormContext()
|
||||
const userInputValue = getValues(`user_inputs.${userInput.name}`) || undefined
|
||||
const userInputValue =
|
||||
userInput.type === 'boolean'
|
||||
? getValues(`user_inputs.${userInput.name}`) === 'YES'
|
||||
? Tr(T.Yes)
|
||||
: Tr(T.No)
|
||||
: getValues(`user_inputs.${userInput.name}`) || undefined
|
||||
|
||||
const toggleDialog = () => setDialogOpen(!dialogOpen)
|
||||
|
||||
@ -65,7 +72,7 @@ const SubNode = ({ userInput }) => {
|
||||
{userInputValue && (
|
||||
<Grid item xs={12}>
|
||||
<Typography noWrap variant="body1">
|
||||
{`Value: ${userInputValue}`}
|
||||
{`${Tr(T.Value)}: ${userInputValue}`}
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
|
@ -32,6 +32,8 @@ import {
|
||||
Radio,
|
||||
} from '@mui/material'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
/**
|
||||
* @param {object} root0 - Props
|
||||
@ -70,7 +72,7 @@ const UserInputDialog = ({ open, onClose, userInput }) => {
|
||||
{...field}
|
||||
labelId="value-select-label"
|
||||
id="value-select"
|
||||
label="Value"
|
||||
label={Tr(T.Value)}
|
||||
multiple={userInput?.type === 'listmultiple'}
|
||||
>
|
||||
{userInput?.options?.map((option, index) => (
|
||||
@ -90,8 +92,12 @@ const UserInputDialog = ({ open, onClose, userInput }) => {
|
||||
value={field.value}
|
||||
onChange={(e) => field.onChange(e.target.value)}
|
||||
>
|
||||
<FormControlLabel value="YES" control={<Radio />} label="Yes" />
|
||||
<FormControlLabel value="NO" control={<Radio />} label="No" />
|
||||
<FormControlLabel
|
||||
value="YES"
|
||||
control={<Radio />}
|
||||
label={Tr(T.Yes)}
|
||||
/>
|
||||
<FormControlLabel value="NO" control={<Radio />} label={Tr(T.No)} />
|
||||
</RadioGroup>
|
||||
)
|
||||
default:
|
||||
@ -100,7 +106,7 @@ const UserInputDialog = ({ open, onClose, userInput }) => {
|
||||
{...field}
|
||||
margin="dense"
|
||||
id="value"
|
||||
label="Value"
|
||||
label={Tr(T.Value)}
|
||||
type={userInput?.type === 'number' ? 'number' : 'text'}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
|
@ -16,7 +16,8 @@
|
||||
import { useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Stack, Typography, Divider } from '@mui/material'
|
||||
|
||||
import { T } from 'client/constants'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import {
|
||||
timeToSecondsByPeriodicity,
|
||||
transformChartersToSchedActions,
|
||||
@ -48,8 +49,11 @@ const FixedLeases = ({ leases }) => {
|
||||
spacing={0.5}
|
||||
>
|
||||
<Typography noWrap variant="subtitle1" padding="1rem">
|
||||
{`> ${sentenceCase(ACTION)} in ${TIME} ${PERIOD}`}
|
||||
{WARNING && ` | Warning before ${WARNING} ${WARNING_PERIOD}`}
|
||||
{`> ${Tr(sentenceCase(ACTION))} ${Tr(T.In)} ${TIME} ${Tr(
|
||||
PERIOD
|
||||
)}`}
|
||||
{WARNING &&
|
||||
` | ${Tr(T.WarningBefore)} ${WARNING} ${Tr(WARNING_PERIOD)}`}
|
||||
</Typography>
|
||||
</Stack>
|
||||
)
|
||||
|
@ -532,7 +532,7 @@ export const PERIOD_FIELD = {
|
||||
typeAction !== SCHEDULE_TYPE.RELATIVE && INPUT_TYPES.HIDDEN,
|
||||
values: arrayToOptions(Object.keys(PERIOD_TYPES), {
|
||||
addEmpty: false,
|
||||
getText: (key) => sentenceCase(key),
|
||||
getText: (key) => PERIOD_TYPES[key],
|
||||
getValue: (key) => PERIOD_TYPES[key],
|
||||
}),
|
||||
validation: lazy((_, { context }) =>
|
||||
|
@ -24,6 +24,8 @@ import {
|
||||
} from '@mui/material'
|
||||
import { DeleteCircledOutline } from 'iconoir-react'
|
||||
import { Component } from 'react'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
/**
|
||||
* AffinityGroup component displays the affinity groups and their descriptions.
|
||||
@ -45,19 +47,19 @@ export const AffinityGroup = ({
|
||||
const isAffined = groupType === 'AFFINED'
|
||||
|
||||
const description = isAffined
|
||||
? 'Affined groups improve performance and communication by placing related VM roles together on the same host. Ideal for roles that require high interactivity and shared resources. '
|
||||
: 'Anti-Affined groups enhance reliability and fault tolerance by distributing VM roles across different hosts. Suitable for roles that need isolation to prevent resource contention and single points of failure.'
|
||||
? Tr(T.AffinedGroupsDescription)
|
||||
: Tr(T.AntiAffinedGroupsDescription)
|
||||
|
||||
const useCases = isAffined
|
||||
? [
|
||||
'Database clusters requiring shared storage.',
|
||||
'High-performance computing with intensive data exchange.',
|
||||
'Real-time processing applications demanding low-latency communication.',
|
||||
Tr(T.AffinedGroupsPotentialCase1),
|
||||
Tr(T.AffinedGroupsPotentialCase2),
|
||||
Tr(T.AffinedGroupsPotentialCase3),
|
||||
]
|
||||
: [
|
||||
'Operational VMs separated from backup VMs.',
|
||||
'Diverse application servers to prevent simultaneous failures.',
|
||||
'Resource-heavy VMs spread out to avoid performance bottlenecks.',
|
||||
Tr(T.AntiAffinedGroupsPotentialCase1),
|
||||
Tr(T.AntiAffinedGroupsPotentialCase2),
|
||||
Tr(T.AntiAffinedGroupsPotentialCase3),
|
||||
]
|
||||
|
||||
return (
|
||||
@ -85,7 +87,7 @@ export const AffinityGroup = ({
|
||||
color="textSecondary"
|
||||
sx={{ mt: 14, opacity: 0.7, textAlign: 'start' }}
|
||||
>
|
||||
<strong>Potential Use Cases:</strong>
|
||||
<strong>{Tr(T.PotentialUseCases)}:</strong>
|
||||
<ul>
|
||||
{useCases.map((useCase, index) => (
|
||||
<li key={index}>{useCase}</li>
|
||||
|
@ -32,6 +32,8 @@ import {
|
||||
} from '@mui/material'
|
||||
import { Group } from 'iconoir-react'
|
||||
import { AffinityGroup } from './affinityGroup'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
/**
|
||||
* Role Affinity Panel component for managing roles.
|
||||
@ -163,10 +165,10 @@ const RoleAffinityPanel = ({
|
||||
fullWidth
|
||||
>
|
||||
<ToggleButton data-cy="policy-AFFINED" value="AFFINED">
|
||||
Affined
|
||||
{Tr(T.Affined)}
|
||||
</ToggleButton>
|
||||
<ToggleButton data-cy="policy-ANTI_AFFINED" value="ANTI_AFFINED">
|
||||
Anti-Affined
|
||||
{Tr(T.AntiAffined)}
|
||||
</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
<List
|
||||
@ -219,7 +221,7 @@ const RoleAffinityPanel = ({
|
||||
size="large"
|
||||
fullWidth
|
||||
>
|
||||
Add Group
|
||||
{Tr(T.AddGroup)}
|
||||
</Button>
|
||||
</CardActions>
|
||||
</Card>
|
||||
@ -238,7 +240,7 @@ const RoleAffinityPanel = ({
|
||||
color: affinedGroups.length ? 'inherit' : 'text.disabled',
|
||||
}}
|
||||
>
|
||||
Affined Groups
|
||||
{Tr(T.AffinedGroups)}
|
||||
</Typography>
|
||||
<Box maxHeight={'500px'} minHeight={'500px'} overflow={'auto'}>
|
||||
<AffinityGroup
|
||||
@ -265,7 +267,7 @@ const RoleAffinityPanel = ({
|
||||
: 'text.disabled',
|
||||
}}
|
||||
>
|
||||
Anti-Affined Groups
|
||||
{Tr(T.AntiAffinedGroups)}
|
||||
</Typography>
|
||||
<Box maxHeight={'500px'} minHeight={'500px'} overflow={'auto'}>
|
||||
<AffinityGroup
|
||||
|
@ -32,8 +32,9 @@ import {
|
||||
Tooltip,
|
||||
} from '@mui/material'
|
||||
import { useLazyGetHostsQuery } from 'client/features/OneApi/host'
|
||||
import { HOST_STATES } from 'client/constants'
|
||||
import { HOST_STATES, T } from 'client/constants'
|
||||
import { useGeneralApi } from 'client/features/General'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
/**
|
||||
* HostAffinityPanel component.
|
||||
@ -84,7 +85,7 @@ const HostAffinityPanel = ({ roles, selectedRoleIndex, onChange }) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
enqueueError(`Error fetching host data: ${error?.message ?? error}`)
|
||||
enqueueError(T.ErrorHostFetching, [error?.message ?? error])
|
||||
}
|
||||
}, [error, enqueueError])
|
||||
|
||||
@ -130,7 +131,7 @@ const HostAffinityPanel = ({ roles, selectedRoleIndex, onChange }) => {
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Host Affinity
|
||||
{Tr(T.HostAffinity)}
|
||||
</Typography>
|
||||
<Box>
|
||||
<ToggleButtonGroup
|
||||
@ -141,16 +142,13 @@ const HostAffinityPanel = ({ roles, selectedRoleIndex, onChange }) => {
|
||||
sx={{ marginBottom: 2 }}
|
||||
>
|
||||
<ToggleButton value="Affined" aria-label="Affined">
|
||||
Affined
|
||||
{Tr(T.Affined)}
|
||||
</ToggleButton>
|
||||
<ToggleButton value="Anti-Affined" aria-label="Anti-Affined">
|
||||
Anti-Affined
|
||||
{Tr(T.AntiAffined)}
|
||||
</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
<Tooltip
|
||||
title="Add a role name to assign Host-VM affinity"
|
||||
placement="right"
|
||||
>
|
||||
<Tooltip title={Tr(T.AddRoleAffinity)} placement="right">
|
||||
<span>
|
||||
<Button
|
||||
variant="contained"
|
||||
@ -160,7 +158,7 @@ const HostAffinityPanel = ({ roles, selectedRoleIndex, onChange }) => {
|
||||
sx={{ ml: 2 }}
|
||||
disabled={isDisabled || selectedHostIds?.length < 1}
|
||||
>
|
||||
Add
|
||||
{Tr(T.Add)}
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
@ -170,10 +168,10 @@ const HostAffinityPanel = ({ roles, selectedRoleIndex, onChange }) => {
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell padding="checkbox"></TableCell>
|
||||
<TableCell>ID</TableCell>
|
||||
<TableCell>Name</TableCell>
|
||||
<TableCell>Cluster</TableCell>
|
||||
<TableCell>Status</TableCell>
|
||||
<TableCell>{Tr(T.ID)}</TableCell>
|
||||
<TableCell>{Tr(T.Name)}</TableCell>
|
||||
<TableCell>{Tr(T.Cluster)}</TableCell>
|
||||
<TableCell>{Tr(T.Status)}</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
@ -214,6 +212,7 @@ const HostAffinityPanel = ({ roles, selectedRoleIndex, onChange }) => {
|
||||
page={page}
|
||||
onPageChange={handleChangePage}
|
||||
onRowsPerPageChange={handleChangeRowsPerPage}
|
||||
labelRowsPerPage={Tr(T.RowsPerPage)}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
|
@ -27,6 +27,7 @@ import {
|
||||
import PropTypes from 'prop-types'
|
||||
import { Cancel, InfoEmpty } from 'iconoir-react'
|
||||
import { T } from 'client/constants'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { Component } from 'react'
|
||||
/**
|
||||
* RoleSummary displays detailed information about a VM role, including its configuration and affinity settings.
|
||||
@ -70,7 +71,7 @@ const RoleSummary = ({ role, selectedRoleIndex, onRemoveAffinity }) => {
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6" component="div" gutterBottom>
|
||||
#{selectedRoleIndex + 1 ?? 0} Role Configuration
|
||||
#{selectedRoleIndex + 1 ?? 0} {Tr(T.RoleConfiguration)}
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
@ -78,7 +79,7 @@ const RoleSummary = ({ role, selectedRoleIndex, onRemoveAffinity }) => {
|
||||
color={role?.NAME ? 'text.primary' : 'text.disabled'}
|
||||
gutterBottom
|
||||
>
|
||||
Name: {role?.NAME || 'Enter a name for this role.'}
|
||||
{Tr(T.Name)}: {role?.NAME || Tr(T.RoleEnterName)}
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
@ -90,18 +91,21 @@ const RoleSummary = ({ role, selectedRoleIndex, onRemoveAffinity }) => {
|
||||
}
|
||||
gutterBottom
|
||||
>
|
||||
Policy: {formatPolicy(role?.POLICY)}
|
||||
{Tr(T.Policy)}: {Tr(formatPolicy(role?.POLICY))}
|
||||
</Typography>
|
||||
|
||||
{role?.HOST_AFFINED && role.HOST_AFFINED.length > 0 ? (
|
||||
<>
|
||||
<Typography variant="body2" color="text.secondary" gutterBottom>
|
||||
Affined Hosts:
|
||||
{Tr(T.AffinedHosts)}:
|
||||
</Typography>
|
||||
<List sx={{ bgcolor: 'background.paper' }}>
|
||||
{role.HOST_AFFINED.map((hostId, index) => (
|
||||
<ListItem key={hostId} sx={{ p: 0, pl: 1 }}>
|
||||
<ListItemText primary={`Host ${hostId}`} sx={{ mr: 1 }} />
|
||||
<ListItemText
|
||||
primary={`${Tr(T.Host)} ${hostId}`}
|
||||
sx={{ mr: 1 }}
|
||||
/>
|
||||
<IconButton
|
||||
edge="end"
|
||||
aria-label="remove"
|
||||
@ -116,9 +120,8 @@ const RoleSummary = ({ role, selectedRoleIndex, onRemoveAffinity }) => {
|
||||
</>
|
||||
) : (
|
||||
<Typography variant="body2" color="text.disabled" gutterBottom>
|
||||
No affined hosts. Assign a set of hosts where the VMs of this role
|
||||
can be allocated.
|
||||
<Tooltip title="Affined hosts are VMs grouped together on a set of hosts to better modularize deployments and increase performance.">
|
||||
{Tr(T.NoAffinedHosts)}
|
||||
<Tooltip title={Tr(T.NoAffinedHostsConcept)}>
|
||||
<InfoEmpty fontSize="small" />
|
||||
</Tooltip>
|
||||
</Typography>
|
||||
@ -127,7 +130,7 @@ const RoleSummary = ({ role, selectedRoleIndex, onRemoveAffinity }) => {
|
||||
{role?.HOST_ANTI_AFFINED && role.HOST_ANTI_AFFINED.length > 0 ? (
|
||||
<>
|
||||
<Typography variant="body2" color="text.secondary" gutterBottom>
|
||||
Anti-Affined Hosts:
|
||||
{Tr(T.AntiAffinedHosts)}:
|
||||
</Typography>
|
||||
<List sx={{ bgcolor: 'background.paper' }}>
|
||||
{role.HOST_ANTI_AFFINED.map((hostId, index) => (
|
||||
@ -147,9 +150,8 @@ const RoleSummary = ({ role, selectedRoleIndex, onRemoveAffinity }) => {
|
||||
</>
|
||||
) : (
|
||||
<Typography variant="body2" color="text.disabled" gutterBottom>
|
||||
No anti-affined hosts. Assign a set of hosts where the VMs of this
|
||||
role can't be allocated.
|
||||
<Tooltip title="Anti-affined hosts are VMs separated across different hosts to ensure redundancy and fault-tolerance. ">
|
||||
{Tr(T.NoAntiAffinedHosts)}
|
||||
<Tooltip title={Tr(T.NoAntiAffinedHostsConcept)}>
|
||||
<InfoEmpty fontSize="small" />
|
||||
</Tooltip>
|
||||
</Typography>
|
||||
@ -161,11 +163,11 @@ const RoleSummary = ({ role, selectedRoleIndex, onRemoveAffinity }) => {
|
||||
color="textSecondary"
|
||||
sx={{ opacity: 0.7 }}
|
||||
>
|
||||
<strong>VM Group Configuration:</strong>
|
||||
<strong>{Tr(T.VMGroupConfiguration)}:</strong>
|
||||
<ul>
|
||||
<li>Define roles and placement constraints.</li>
|
||||
<li>Optimize performance and fault tolerance.</li>
|
||||
<li>Manage multi-VM applications efficiently.</li>
|
||||
<li>{Tr(T.RoleDefineRoles)}</li>
|
||||
<li>{Tr(T.RoleOptimize)}</li>
|
||||
<li>{Tr(T.RoleManageApps)}</li>
|
||||
</ul>
|
||||
</Typography>
|
||||
</CardActions>
|
||||
|
@ -17,6 +17,8 @@ import { useCallback, Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Box, Button, List, ListItem, IconButton } from '@mui/material'
|
||||
import { Cancel } from 'iconoir-react'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
/**
|
||||
* RoleColumn component for displaying and managing roles.
|
||||
@ -75,7 +77,7 @@ const RoleColumn = ({
|
||||
size="large"
|
||||
data-cy="add-role"
|
||||
>
|
||||
Add Role
|
||||
{Tr(T.AddRole)}
|
||||
</Button>
|
||||
<Box
|
||||
sx={{
|
||||
@ -128,7 +130,7 @@ const RoleColumn = ({
|
||||
whiteSpace: 'nowrap',
|
||||
}}
|
||||
>
|
||||
{role?.NAME || 'New Role'}
|
||||
{role?.NAME || Tr(T.NewRole)}
|
||||
</div>
|
||||
</ListItem>
|
||||
))}
|
||||
|
@ -24,6 +24,8 @@ import {
|
||||
FormControl,
|
||||
InputLabel,
|
||||
} from '@mui/material'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
/**
|
||||
* Role Panel component for managing roles.
|
||||
@ -52,10 +54,10 @@ const RoleVmVmPanel = ({ roles, onChange, selectedRoleIndex }) => {
|
||||
return (
|
||||
<Box p={2}>
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<Typography variant="h6">Role Details</Typography>
|
||||
<Typography variant="h6">{Tr(T.RoleDetails)}</Typography>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<TextField
|
||||
label="Role Name"
|
||||
label={Tr(T.RoleName)}
|
||||
name="NAME"
|
||||
value={roles?.[selectedRoleIndex]?.NAME ?? ''}
|
||||
onChange={handleInputChange}
|
||||
@ -69,7 +71,7 @@ const RoleVmVmPanel = ({ roles, onChange, selectedRoleIndex }) => {
|
||||
<FormControl fullWidth variant="outlined" sx={{ mt: 2 }}>
|
||||
<InputLabel>VM-VM Affinity</InputLabel>
|
||||
<Select
|
||||
label="VM-VM Affinity"
|
||||
label={Tr(T.VMAffinity)}
|
||||
name="POLICY"
|
||||
data-cy="policy-selector"
|
||||
value={roles?.[selectedRoleIndex]?.POLICY ?? 'None'}
|
||||
@ -77,19 +79,19 @@ const RoleVmVmPanel = ({ roles, onChange, selectedRoleIndex }) => {
|
||||
onChange={handleInputChange}
|
||||
>
|
||||
<MenuItem data-cy="policy-selector-policy-None" value="None">
|
||||
None
|
||||
{Tr(T.None)}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
data-cy="policy-selector-policy-AFFINED"
|
||||
value="AFFINED"
|
||||
>
|
||||
Affined
|
||||
{Tr(T.Affined)}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
data-cy="policy-selector-policy-ANTI_AFFINED"
|
||||
value="ANTI_AFFINED"
|
||||
>
|
||||
Anti-Affined
|
||||
{Tr(T.AntiAffined)}
|
||||
</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
@ -39,6 +39,7 @@ import {
|
||||
CircularProgress,
|
||||
} from '@mui/material'
|
||||
import { Trash as DeleteIcon, Download } from 'iconoir-react'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
/**
|
||||
* @param {object} root0 - Props
|
||||
@ -99,7 +100,7 @@ const PopUpDialog = ({ open, handleClose }) => {
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
enqueueError('Failed to fetch official suggestions')
|
||||
enqueueError(T.ErrorUserInputAutocompleteFetch)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -156,7 +156,7 @@ const HOST_POLICY_TYPE_FIELD = {
|
||||
name: 'HOST_POLICY_TYPE',
|
||||
type: INPUT_TYPES.TOGGLE,
|
||||
values: () =>
|
||||
arrayToOptions(['Packing', 'Stripping', 'Load-aware'], {
|
||||
arrayToOptions([T.Packing, T.Stripping, T.LoadAware], {
|
||||
addEmpty: false,
|
||||
getText: (opt) => opt,
|
||||
getValue: (_opt, idx) =>
|
||||
@ -221,12 +221,9 @@ const TABLE_TYPE = {
|
||||
name: 'CLUSTER_HOST_TYPE',
|
||||
type: INPUT_TYPES.TOGGLE,
|
||||
values: () =>
|
||||
arrayToOptions(
|
||||
[T.Cluster, T.Host]?.map((t) => T.Select + ' ' + t),
|
||||
{
|
||||
addEmpty: false,
|
||||
}
|
||||
),
|
||||
arrayToOptions([T.SelectCluster, T.SelectHost], {
|
||||
addEmpty: false,
|
||||
}),
|
||||
validation: string()
|
||||
.trim()
|
||||
.required()
|
||||
|
@ -27,9 +27,10 @@ import {
|
||||
import { sprintf } from 'sprintf-js'
|
||||
import root from 'window-or-global'
|
||||
|
||||
import { LANGUAGES, LANGUAGES_URL } from 'client/constants'
|
||||
import { LANGUAGES, LANGUAGES_URL, T } from 'client/constants'
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { isDevelopment } from 'client/utils'
|
||||
import { findKey } from 'lodash'
|
||||
|
||||
const TranslateContext = createContext()
|
||||
|
||||
@ -65,13 +66,20 @@ const labelCanBeTranslated = (val) =>
|
||||
* @returns {boolean} - True if the value can be translated
|
||||
*/
|
||||
const translateString = (word = '', values) => {
|
||||
// Get the translation context so hash will be the map with the language that is using the user
|
||||
const { hash = {} } = useContext(TranslateContext)
|
||||
const { [word]: wordVal } = hash
|
||||
|
||||
// Look for the key thas has the value equal to word in the T object
|
||||
const key = findKey(T, (value) => value === word)
|
||||
|
||||
// Using the key from the previous step, get the translated word in the language map
|
||||
const { [key]: wordVal } = hash
|
||||
|
||||
// If there is no word, use the original
|
||||
const ensuredWord = wordVal || word
|
||||
|
||||
if (!ensuredWord || !values) return ensuredWord
|
||||
|
||||
// Return translation
|
||||
try {
|
||||
return sprintf(ensuredWord, ...values)
|
||||
} catch {
|
||||
@ -148,7 +156,10 @@ const Tr = (word = '', values = []) => {
|
||||
const Translate = memo(({ word = '', values = [] }) => {
|
||||
const [w, v = values] = Array.isArray(word) ? word : [word, values]
|
||||
const ensuredValues = !Array.isArray(v) ? [v] : v
|
||||
const translation = translateString(w, ensuredValues.filter(Boolean))
|
||||
const translation = translateString(
|
||||
w,
|
||||
ensuredValues.filter((value) => value || value === 0)
|
||||
)
|
||||
|
||||
return <>{translation}</>
|
||||
})
|
||||
|
@ -70,7 +70,7 @@ const ListHeader = memo(
|
||||
type="search"
|
||||
onChange={searchProps.handleChange}
|
||||
fullWidth
|
||||
placeholder={`${T.Search}...`}
|
||||
placeholder={`${Tr(T.Search)}...`}
|
||||
classes={{
|
||||
root: classes.inputRoot,
|
||||
input: classes.inputInput,
|
||||
|
@ -18,6 +18,8 @@ import { Component, useEffect } from 'react'
|
||||
import { Box, Typography, CircularProgress } from '@mui/material'
|
||||
import { InfoEmpty, CloudError } from 'iconoir-react'
|
||||
import { useGeneralApi } from 'client/features/General'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
/**
|
||||
* Renders a display message based on the presence of an error.
|
||||
@ -51,14 +53,14 @@ export const LoadingDisplay = ({ isLoading, error, isEmpty }) => {
|
||||
)
|
||||
}
|
||||
|
||||
let displayMessage = 'No data available'
|
||||
let displayMessage = Tr(T.NoDataAvailable)
|
||||
let DisplayIcon = InfoEmpty
|
||||
|
||||
if (error && error.length > 0) {
|
||||
displayMessage = error
|
||||
DisplayIcon = CloudError
|
||||
} else if (isEmpty) {
|
||||
displayMessage = 'No data available'
|
||||
displayMessage = Tr(T.NoDataAvailable)
|
||||
DisplayIcon = InfoEmpty
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ const Notifier = () => {
|
||||
|
||||
useEffect(() => {
|
||||
notifications.forEach(
|
||||
({ key, message, options = {}, dismissed = false }) => {
|
||||
({ key, message, options = {}, dismissed = false, values }) => {
|
||||
if (dismissed) {
|
||||
closeSnackbar(key)
|
||||
|
||||
@ -57,7 +57,7 @@ const Notifier = () => {
|
||||
|
||||
if (displayed.includes(key)) return
|
||||
|
||||
enqueueSnackbar(<Translate word={message} />, {
|
||||
enqueueSnackbar(<Translate word={message} values={values} />, {
|
||||
key,
|
||||
...options,
|
||||
action: CloseButton({ handleClick: () => closeSnackbar(key) }),
|
||||
|
@ -20,6 +20,9 @@ import { TextField, Box } from '@mui/material'
|
||||
import { useSearch } from 'client/hooks'
|
||||
import { ListInfiniteScroll } from 'client/components/List'
|
||||
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
const Search = ({
|
||||
list,
|
||||
listOptions,
|
||||
@ -38,7 +41,7 @@ const Search = ({
|
||||
value={query}
|
||||
onChange={handleChange}
|
||||
fullWidth
|
||||
placeholder="Search..."
|
||||
placeholder={`${Tr(T.Search)}...`}
|
||||
/>
|
||||
</Box>
|
||||
{result?.length === 0 ? (
|
||||
|
@ -18,6 +18,7 @@ import PropTypes from 'prop-types'
|
||||
|
||||
import { Tooltip, Typography, Box } from '@mui/material'
|
||||
import { Component } from 'react'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
/**
|
||||
* `LinearProgressWithTooltip` component displays a linear progress bar with a label and an optional tooltip.
|
||||
@ -51,7 +52,7 @@ const LinearProgressWithTooltip = ({
|
||||
<Tooltip
|
||||
arrow
|
||||
placement="right"
|
||||
title={<Typography variant="subtitle2">{tooltipTitle}</Typography>}
|
||||
title={<Typography variant="subtitle2">{Tr(tooltipTitle)}</Typography>}
|
||||
>
|
||||
<Box component="span" ml={2} mt={1} mr={2} lineHeight={1}>
|
||||
{icon}
|
||||
|
@ -76,9 +76,10 @@ const BackupsTable = (props) => {
|
||||
)
|
||||
|
||||
/**
|
||||
* Refetch vms and backups. If a new backup is created, the id of the backup will be in the data of a vm, so we need to refetch also the vms query.
|
||||
* Refetch vms and backups. If a new backup is created, the id of the backup will be in the data of a vm, so we need to refetch also the vms query when we are showing the backups of a vm.
|
||||
*/
|
||||
const refetchAll = () => {
|
||||
// Refetch vm only if the function exists (if not, we are looking for all backups, no matter which vm is associated)
|
||||
refetchVm && refetchVm()
|
||||
refetch()
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ import Timer from 'client/components/Timer'
|
||||
import { StatusCircle, StatusChip } from 'client/components/Status'
|
||||
import { rowStyles } from 'client/components/Tables/styles'
|
||||
import { T } from 'client/constants'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
import * as ImageModel from 'client/models/Image'
|
||||
import * as Helper from 'client/models/Helper'
|
||||
@ -126,38 +127,40 @@ const Row = ({ original, value, onClickLabel, ...props }) => {
|
||||
<span title={time.toFormat('ff')}>
|
||||
<Timer translateWord={T.RegisteredAt} initial={time} />
|
||||
</span>
|
||||
<span title={`${T.Owner}: ${UNAME}`}>
|
||||
<span title={`${Tr(T.Owner)}: ${UNAME}`}>
|
||||
<User />
|
||||
<span>{` ${UNAME}`}</span>
|
||||
</span>
|
||||
<span title={`${T.Group}: ${GNAME}`}>
|
||||
<span title={`${Tr(T.Group)}: ${GNAME}`}>
|
||||
<Group />
|
||||
<span>{` ${GNAME}`}</span>
|
||||
</span>
|
||||
<span title={`${T.Datastore}: ${DATASTORE}`}>
|
||||
<span title={`${Tr(T.Datastore)}: ${DATASTORE}`}>
|
||||
<DatastoreIcon />
|
||||
<span>{` ${DATASTORE}`}</span>
|
||||
</span>
|
||||
<span
|
||||
title={
|
||||
PERSISTENT
|
||||
? T.Persistent.toLowerCase()
|
||||
: T.NonPersistent.toLowerCase()
|
||||
? Tr(T.Persistent).toLowerCase()
|
||||
: Tr(T.NonPersistent).toLowerCase()
|
||||
}
|
||||
>
|
||||
<PersistentIcon />
|
||||
<span>
|
||||
{PERSISTENT
|
||||
? T.Persistent.toLowerCase()
|
||||
: T.NonPersistent.toLowerCase()}
|
||||
? Tr(T.Persistent).toLowerCase()
|
||||
: Tr(T.NonPersistent).toLowerCase()}
|
||||
</span>
|
||||
</span>
|
||||
<span title={`${T.DiskType}: ${DISK_TYPE.toLowerCase()}`}>
|
||||
<span title={`${Tr(T.DiskType)}: ${DISK_TYPE.toLowerCase()}`}>
|
||||
<DiskTypeIcon />
|
||||
<span>{` ${DISK_TYPE.toLowerCase()}`}</span>
|
||||
</span>
|
||||
<span
|
||||
title={`${T.Running} / ${T.Used} ${T.VMs}: ${RUNNING_VMS} / ${TOTAL_VMS}`}
|
||||
title={`${Tr(T.Running)} / ${Tr(T.Used)} ${Tr(
|
||||
T.VMs
|
||||
)}: ${RUNNING_VMS} / ${TOTAL_VMS}`}
|
||||
>
|
||||
<ModernTv />
|
||||
<span>{` ${RUNNING_VMS} / ${TOTAL_VMS}`}</span>
|
||||
|
@ -21,6 +21,9 @@ import { Typography } from '@mui/material'
|
||||
|
||||
import { rowStyles } from 'client/components/Tables/styles'
|
||||
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
const Row = ({ original, value, ...props }) => {
|
||||
const classes = rowStyles()
|
||||
const { ID, NAME, HOSTS, DATASTORES, VNETS, PROVIDER_NAME } = value
|
||||
@ -35,26 +38,29 @@ const Row = ({ original, value, ...props }) => {
|
||||
</div>
|
||||
<div className={classes.caption}>
|
||||
<span data-cy="cluster-card-id">{`#${ID}`}</span>
|
||||
<span data-cy="cluster-card-hosts" title={`Total Hosts: ${HOSTS}`}>
|
||||
<span
|
||||
data-cy="cluster-card-hosts"
|
||||
title={`${Tr(T.Total)} ${Tr(T.Hosts)}: ${HOSTS}`}
|
||||
>
|
||||
<HardDrive />
|
||||
<span>{`${HOSTS}`}</span>
|
||||
</span>
|
||||
<span
|
||||
data-cy="cluster-card-vnets"
|
||||
title={`Total Virtual Networks: ${VNETS}`}
|
||||
title={`${Tr(T.Total)} ${Tr(T.VirtualNetworks)}: ${VNETS}`}
|
||||
>
|
||||
<NetworkAlt />
|
||||
<span>{`${VNETS}`}</span>
|
||||
</span>
|
||||
<span
|
||||
data-cy="cluster-card-datastores"
|
||||
title={`Total Datastores: ${DATASTORES}`}
|
||||
title={`${Tr(T.Total)} ${Tr(T.Datastores)}: ${DATASTORES}`}
|
||||
>
|
||||
<Folder />
|
||||
<span>{`${DATASTORES}`}</span>
|
||||
</span>
|
||||
{PROVIDER_NAME && (
|
||||
<span title={`Provider: ${PROVIDER_NAME}`}>
|
||||
<span title={`${Tr(T.Provider)}: ${PROVIDER_NAME}`}>
|
||||
<Cloud />
|
||||
<span>{` ${PROVIDER_NAME}`}</span>
|
||||
</span>
|
||||
|
@ -17,6 +17,8 @@ import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { DataGrid } from '@mui/x-data-grid'
|
||||
import { Box } from '@mui/material'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
/**
|
||||
* Renders a data grid table using the provided data and columns.
|
||||
@ -43,6 +45,20 @@ const DataGridTable = ({ data, columns }) => {
|
||||
rows={flattenedData.map((row, index) => ({ ...row, id: index }))}
|
||||
columns={columns}
|
||||
rowsPerPageOptions={[25, 50, 100]}
|
||||
localeText={{
|
||||
columnMenuLabel: Tr(T.ColumnMenuLabel),
|
||||
columnMenuShowColumns: Tr(T.ColumnMenuShowColumns),
|
||||
columnMenuManageColumns: Tr(T.ColumnMenuManageColumns),
|
||||
columnMenuFilter: Tr(T.ColumnMenuFilter),
|
||||
columnMenuHideColumn: Tr(T.ColumnMenuHideColumn),
|
||||
columnMenuUnsort: Tr(T.ColumnMenuUnsort),
|
||||
columnMenuSortAsc: Tr(T.ColumnMenuSortAsc),
|
||||
columnMenuSortDesc: Tr(T.ColumnMenuSortDesc),
|
||||
columnHeaderSortIconLabel: Tr(T.ColumnHeaderSortIconLabel),
|
||||
MuiTablePagination: {
|
||||
labelRowsPerPage: Tr(T.RowsPerPage),
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
|
@ -67,8 +67,8 @@ const GlobalSort = memo(
|
||||
<HeaderPopover
|
||||
id="sort-by-button"
|
||||
icon={<SortDown />}
|
||||
headerTitle={T.SortBy}
|
||||
buttonLabel={T.Sort}
|
||||
headerTitle={<Translate word={T.SortBy} />}
|
||||
buttonLabel={<Translate word={T.Sort} />}
|
||||
buttonProps={{
|
||||
'data-cy': 'sort-by-button',
|
||||
disableElevation: true,
|
||||
|
@ -22,6 +22,7 @@ import { NavArrowLeft, NavArrowRight } from 'iconoir-react'
|
||||
import { UsePaginationState } from 'react-table'
|
||||
|
||||
import { T } from 'client/constants'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
const Pagination = ({
|
||||
className,
|
||||
@ -59,10 +60,10 @@ const Pagination = ({
|
||||
color="inherit"
|
||||
>
|
||||
<NavArrowLeft />
|
||||
{T.Previous}
|
||||
{Tr(T.Previous)}
|
||||
</Button>
|
||||
<Typography variant="body2" component="span">
|
||||
{`${pageIndex + 1} of ${showPageCount ? pageCount : 'many'}`}
|
||||
{`${pageIndex + 1} ${Tr(T.Of)} ${showPageCount ? pageCount : 'many'}`}
|
||||
</Typography>
|
||||
<Button
|
||||
aria-label="next page"
|
||||
@ -71,7 +72,7 @@ const Pagination = ({
|
||||
size="small"
|
||||
color="inherit"
|
||||
>
|
||||
{T.Next}
|
||||
{Tr(T.Next)}
|
||||
<NavArrowRight />
|
||||
</Button>
|
||||
</Stack>
|
||||
|
@ -33,6 +33,7 @@ import Timer from 'client/components/Timer'
|
||||
import { StatusCircle, StatusChip } from 'client/components/Status'
|
||||
import { rowStyles } from 'client/components/Tables/styles'
|
||||
import { T } from 'client/constants'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import {
|
||||
jsonToXml,
|
||||
getUniqueLabels,
|
||||
@ -120,27 +121,27 @@ const Row = ({ original, value, onClickLabel, ...props }) => {
|
||||
<span title={time.toFormat('ff')}>
|
||||
<Timer translateWord={T.RegisteredAt} initial={time} />
|
||||
</span>
|
||||
<span title={`${T.Owner}: ${UNAME}`}>
|
||||
<span title={`${Tr(T.Owner)}: ${UNAME}`}>
|
||||
<User />
|
||||
<span>{` ${UNAME}`}</span>
|
||||
</span>
|
||||
<span title={`${T.Group}: ${GNAME}`}>
|
||||
<span title={`${Tr(T.Group)}: ${GNAME}`}>
|
||||
<Group />
|
||||
<span>{` ${GNAME}`}</span>
|
||||
</span>
|
||||
<span title={`${T.Datastore}: ${DATASTORE}`}>
|
||||
<span title={`${Tr(T.Datastore)}: ${DATASTORE}`}>
|
||||
<DatastoreIcon />
|
||||
<span>{` ${DATASTORE}`}</span>
|
||||
</span>
|
||||
<span title={+PERSISTENT ? T.Persistent : T.NonPersistent}>
|
||||
<span title={+PERSISTENT ? Tr(T.Persistent) : Tr(T.NonPersistent)}>
|
||||
<PersistentIcon />
|
||||
<span>{+PERSISTENT ? T.Persistent : T.NonPersistent}</span>
|
||||
<span>{+PERSISTENT ? Tr(T.Persistent) : Tr(T.NonPersistent)}</span>
|
||||
</span>
|
||||
<span title={`${T.VMs}: ${RUNNING_VMS}`}>
|
||||
<span title={`${Tr(T.VMs)}: ${RUNNING_VMS}`}>
|
||||
<ModernTv />
|
||||
<span>{`${RUNNING_VMS}`}</span>
|
||||
</span>
|
||||
<span title={`${T.Size}: ${SIZE}`}>
|
||||
<span title={`${Tr(T.Size)}: ${SIZE}`}>
|
||||
<span>{`${prettyBytes(+SIZE ?? 0)}`}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
@ -20,6 +20,8 @@ import { Typography } from '@mui/material'
|
||||
import { Cloud, Group, Lock, User } from 'iconoir-react'
|
||||
|
||||
import { rowStyles } from 'client/components/Tables/styles'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
import * as Helper from 'client/models/Helper'
|
||||
|
||||
@ -41,16 +43,16 @@ const Row = ({ original, value, ...props }) => {
|
||||
</div>
|
||||
<div className={classes.caption}>
|
||||
<span title={time.toFormat('ff')}>{`#${ID} ${timeAgo}`}</span>
|
||||
<span title={`Owner: ${UNAME}`}>
|
||||
<span title={`${Tr(T.Owner)}: ${UNAME}`}>
|
||||
<User />
|
||||
<span>{` ${UNAME}`}</span>
|
||||
</span>
|
||||
<span title={`Group: ${GNAME}`}>
|
||||
<span title={`${Tr(T.Group)}: ${GNAME}`}>
|
||||
<Group />
|
||||
<span>{` ${GNAME}`}</span>
|
||||
</span>
|
||||
{PROVISION_ID && (
|
||||
<span title={`Provision ID: #${PROVISION_ID}`}>
|
||||
<span title={`${Tr(T.ProvisionId)}: #${PROVISION_ID}`}>
|
||||
<Cloud />
|
||||
<span>{` ${PROVISION_ID}`}</span>
|
||||
</span>
|
||||
|
@ -16,7 +16,7 @@
|
||||
import { Component, useMemo, useState, useEffect } from 'react'
|
||||
import { DatastoreDialog } from './DatastoreSelectionDialog'
|
||||
import { Grid, Box, Button } from '@mui/material'
|
||||
|
||||
import { Tr, Translate } from 'client/components/HOC'
|
||||
import { T, RESOURCE_NAMES } from 'client/constants'
|
||||
import { BoxIso as DownloadIcon } from 'iconoir-react'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
@ -37,7 +37,6 @@ import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
|
||||
import VRouterTemplateColumns from 'client/components/Tables/VRouterTemplates/columns'
|
||||
import VRouterTemplateRow from 'client/components/Tables/VRouterTemplates/row'
|
||||
import InfoEmpty from 'iconoir-react/dist/InfoEmpty'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
import { useStyles } from 'client/components/Tabs/EmptyTab/styles'
|
||||
|
||||
const DEFAULT_DATA_CY = 'vrouter-templates'
|
||||
@ -144,7 +143,7 @@ const VRouterTemplatesTable = (props) => {
|
||||
const enableMarket = async (marketID) => {
|
||||
const response = await enableMarketplace({ id: marketID })
|
||||
if (response?.data === parseInt(marketID, 10)) {
|
||||
enqueueInfo('Enabled OpenNebula Public marketplace')
|
||||
enqueueInfo(T.InfoEnableOpenNebulaMarketplace)
|
||||
setOneMarket({ ...oneMarket, STATE: 'ENABLED' })
|
||||
fetchApps()
|
||||
}
|
||||
@ -204,7 +203,7 @@ const VRouterTemplatesTable = (props) => {
|
||||
})
|
||||
.then((res) => {
|
||||
if (res?.data) {
|
||||
enqueueSuccess('Downloaded default image') && refetch()
|
||||
enqueueSuccess(T.SuccessDownloadDefaultImage) && refetch()
|
||||
} else if (res?.error) {
|
||||
enqueueError(res.error)
|
||||
} else {
|
||||
@ -246,7 +245,7 @@ const VRouterTemplatesTable = (props) => {
|
||||
startIcon={<DownloadIcon />}
|
||||
onClick={() => handleDownloadDefaultImage()}
|
||||
>
|
||||
{T.DownloadDefaultImage}
|
||||
{Tr(T.DownloadDefaultImage)}
|
||||
</Button>
|
||||
</Box>
|
||||
</Grid>
|
||||
|
@ -21,6 +21,9 @@ import { Typography } from '@mui/material'
|
||||
|
||||
import { rowStyles } from 'client/components/Tables/styles'
|
||||
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
const Row = ({ original, value, ...props }) => {
|
||||
const classes = rowStyles()
|
||||
const { ID, NAME, UNAME, GNAME, VMS, TEMPLATE_ID } = value
|
||||
@ -35,19 +38,19 @@ const Row = ({ original, value, ...props }) => {
|
||||
</div>
|
||||
<div className={classes.caption}>
|
||||
<span>{`#${ID}`}</span>
|
||||
<span title={`Owner: ${UNAME}`}>
|
||||
<span title={`${Tr(T.Owner)}: ${UNAME}`}>
|
||||
<User />
|
||||
<span>{` ${UNAME}`}</span>
|
||||
</span>
|
||||
<span title={`Group: ${GNAME}`}>
|
||||
<span title={`${Tr(T.Group)}: ${GNAME}`}>
|
||||
<Group />
|
||||
<span>{` ${GNAME}`}</span>
|
||||
</span>
|
||||
<span title={`Template ID: ${TEMPLATE_ID}`}>
|
||||
<span title={`${Tr(T.Template)}${Tr(T.ID)}: ${TEMPLATE_ID}`}>
|
||||
<EmptyPage />
|
||||
<span>{` ${TEMPLATE_ID}`}</span>
|
||||
</span>
|
||||
<span title={`Total VMs: ${VMS}`}>
|
||||
<span title={`${Tr(T.TotalVms)}: ${VMS}`}>
|
||||
<ModernTv />
|
||||
<span>{` ${VMS}`}</span>
|
||||
</span>
|
||||
|
@ -18,7 +18,8 @@ import PropTypes from 'prop-types'
|
||||
|
||||
import { Typography } from '@mui/material'
|
||||
import { HomeShield } from 'iconoir-react'
|
||||
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
import { StatusCircle } from 'client/components/Status'
|
||||
import { rowStyles } from 'client/components/Tables/styles'
|
||||
|
||||
@ -41,7 +42,7 @@ const Row = ({ original, value, ...props }) => {
|
||||
</div>
|
||||
<div className={classes.caption}>
|
||||
<span>{`#${ID}`}</span>
|
||||
<span title={`Endpoint: ${ENDPOINT}`}>
|
||||
<span title={`${Tr(T.Endpoint)}: ${ENDPOINT}`}>
|
||||
<HomeShield />
|
||||
<span>{` ${ENDPOINT}`}</span>
|
||||
</span>
|
||||
|
@ -17,6 +17,8 @@ import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { MultiChart } from 'client/components/Charts'
|
||||
import { Box } from '@mui/material'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { mapValues } from 'lodash'
|
||||
|
||||
const commonStyles = {
|
||||
height: '500px',
|
||||
@ -88,22 +90,28 @@ export const CustomizedChart = ({
|
||||
error,
|
||||
isLoading,
|
||||
groupBy,
|
||||
}) => (
|
||||
<Box style={commonStyles}>
|
||||
<MultiChart
|
||||
datasets={datasets}
|
||||
visibleDatasets={visibleDatasets}
|
||||
chartType={chartType}
|
||||
selectedMetrics={selectedMetrics}
|
||||
ItemsPerPage={7}
|
||||
error={error}
|
||||
isLoading={isLoading}
|
||||
tableColumns={DataGridColumns}
|
||||
metricNames={metricNames}
|
||||
groupBy={groupBy}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
}) => {
|
||||
const metricNamesTranslated = mapValues(metricNames, (value, key) =>
|
||||
Tr(value)
|
||||
)
|
||||
|
||||
return (
|
||||
<Box style={commonStyles}>
|
||||
<MultiChart
|
||||
datasets={datasets}
|
||||
visibleDatasets={visibleDatasets}
|
||||
chartType={chartType}
|
||||
selectedMetrics={selectedMetrics}
|
||||
ItemsPerPage={7}
|
||||
error={error}
|
||||
isLoading={isLoading}
|
||||
tableColumns={DataGridColumns}
|
||||
metricNames={metricNamesTranslated}
|
||||
groupBy={groupBy}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
CustomizedChart.propTypes = {
|
||||
datasets: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
|
@ -16,6 +16,7 @@
|
||||
import { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Box, Checkbox, FormControlLabel, FormGroup } from '@mui/material'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
/**
|
||||
* Sub-component used to select different metrics.
|
||||
@ -41,7 +42,7 @@ export const MetricSelector = ({ selectedItems, onChange, items }) => (
|
||||
color="primary"
|
||||
/>
|
||||
}
|
||||
label={item.label}
|
||||
label={Tr(item.label)}
|
||||
/>
|
||||
))}
|
||||
</FormGroup>
|
||||
|
@ -40,6 +40,8 @@ import {
|
||||
calculateDisplayMetrics,
|
||||
} from 'client/components/Tabs/Accounting/helpers'
|
||||
import { filterDataset } from 'client/components/Charts/MultiChart/helpers/scripts'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
const ACTION_ADD = 'add'
|
||||
const ACTION_REMOVE = 'remove'
|
||||
@ -275,15 +277,15 @@ const generateAccountingInfoTab = ({ groups }) => {
|
||||
size="small"
|
||||
style={{ marginRight: 2, minWidth: 120 }}
|
||||
>
|
||||
<InputLabel>Group By</InputLabel>
|
||||
<InputLabel>{Tr(T.GroupBy)}</InputLabel>
|
||||
<Select
|
||||
value={groupBy}
|
||||
onChange={handleGroupByChange}
|
||||
label="Group By"
|
||||
label={Tr(T.GroupBy)}
|
||||
>
|
||||
<MenuItem value="NAME">VM</MenuItem>
|
||||
<MenuItem value="UNAME">User</MenuItem>
|
||||
<MenuItem value="GNAME">Group</MenuItem>
|
||||
<MenuItem value="NAME">{Tr(T.VM)}</MenuItem>
|
||||
<MenuItem value="UNAME">{Tr(T.User)}</MenuItem>
|
||||
<MenuItem value="GNAME">{Tr(T.Group)}</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormControl
|
||||
@ -291,16 +293,16 @@ const generateAccountingInfoTab = ({ groups }) => {
|
||||
size="small"
|
||||
style={{ marginRight: 2, minWidth: 120 }}
|
||||
>
|
||||
<InputLabel>Chart Type</InputLabel>
|
||||
<InputLabel>{Tr(T.ChartType)}</InputLabel>
|
||||
<Select
|
||||
value={chartType}
|
||||
onChange={handleChartTypeChange}
|
||||
label="Chart Type"
|
||||
label={Tr(T.ChartType)}
|
||||
>
|
||||
<MenuItem value="line">Line Chart</MenuItem>
|
||||
<MenuItem value="bar">Bar Chart</MenuItem>
|
||||
<MenuItem value="area">Area Chart</MenuItem>
|
||||
<MenuItem value="table">Table Chart</MenuItem>
|
||||
<MenuItem value="line">{Tr(T.LineChart)}</MenuItem>
|
||||
<MenuItem value="bar">{Tr(T.BarChart)}</MenuItem>
|
||||
<MenuItem value="area">{Tr(T.AreaChart)}</MenuItem>
|
||||
<MenuItem value="table">{Tr(T.TableChart)}</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Box>
|
||||
|
@ -33,7 +33,7 @@ import { generatePath, useHistory } from 'react-router-dom'
|
||||
import { PATH } from 'client/apps/sunstone/routesOne'
|
||||
|
||||
import { SubmitButton } from 'client/components/FormControl'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
import { Translate, Tr } from 'client/components/HOC'
|
||||
import { VmsTable } from 'client/components/Tables'
|
||||
import AttachVms from 'client/components/Tabs/BackupJobs/VMs/Actions'
|
||||
import { T } from 'client/constants'
|
||||
@ -189,7 +189,7 @@ const VmsInfoTab = ({ id }) => {
|
||||
key={type}
|
||||
value={type}
|
||||
control={<Radio />}
|
||||
label={states[type].select}
|
||||
label={Tr(states[type].select)}
|
||||
/>
|
||||
))}
|
||||
</RadioGroup>
|
||||
|
@ -23,7 +23,7 @@ import { DialogConfirmation } from 'client/components/Dialogs'
|
||||
import { Actions, Inputs } from 'client/components/Tabs/Common/Attribute'
|
||||
import { useDialog } from 'client/hooks'
|
||||
|
||||
import { Translate } from 'client/components/HOC'
|
||||
import { Translate, Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
const Column = (props) => {
|
||||
@ -121,7 +121,6 @@ const Attribute = memo(
|
||||
noWrap
|
||||
component="span"
|
||||
variant="body2"
|
||||
title={typeof name === 'string' ? name : undefined}
|
||||
flexGrow={1}
|
||||
sx={{
|
||||
...(numberOfParents > 0 && { pl: `${numberOfParents}em` }),
|
||||
@ -133,7 +132,7 @@ const Attribute = memo(
|
||||
}}
|
||||
>
|
||||
{icon}
|
||||
{name}
|
||||
{Tr(name)}
|
||||
</Typography>
|
||||
<ActionWrapper {...(showActionsOnHover && { display: 'none' })}>
|
||||
{canCopy && <Actions.Copy name={name} value={name} />}
|
||||
@ -195,7 +194,7 @@ const Attribute = memo(
|
||||
{value}
|
||||
</Link>
|
||||
) : (
|
||||
value
|
||||
Tr(value)
|
||||
)}
|
||||
</Typography>
|
||||
<ActionWrapper {...(showActionsOnHover && { display: 'none' })}>
|
||||
@ -205,6 +204,7 @@ const Attribute = memo(
|
||||
title={title || name}
|
||||
name={name}
|
||||
handleClick={handleActiveEditForm}
|
||||
tooltip={name}
|
||||
/>
|
||||
)}
|
||||
{canDelete && (
|
||||
|
@ -23,6 +23,7 @@ import {
|
||||
forwardRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
import { Actions } from 'client/components/Tabs/Common/Attribute'
|
||||
|
||||
@ -110,7 +111,7 @@ const Text = forwardRef(
|
||||
inputRef={ref}
|
||||
onChange={handleChange}
|
||||
value={newValue}
|
||||
name={name}
|
||||
name={Tr(name)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
|
@ -20,6 +20,7 @@ import { ReactElement } from 'react'
|
||||
import { Chartist } from 'client/components/Charts'
|
||||
import { T } from 'client/constants'
|
||||
import { useGetHostMonitoringQuery } from 'client/features/OneApi/host'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
/**
|
||||
* Renders the host Graph tab.
|
||||
@ -50,20 +51,20 @@ const HostGraphTab = ({ id }) => {
|
||||
y={['FREE_CPU', 'USED_CPU']}
|
||||
x="TIMESTAMP"
|
||||
enableLegend={true}
|
||||
legendNames={[T.FreeCPU, T.UsedCPU]}
|
||||
legendNames={[Tr(T.FreeCPU), Tr(T.UsedCPU)]}
|
||||
lineColors={['#039be5', '#757575']}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={12}>
|
||||
<Chartist
|
||||
name={T.Memory}
|
||||
name={Tr(T.Memory)}
|
||||
filter={['FREE_MEMORY', 'USED_MEMORY']}
|
||||
data={cpuMemoryData}
|
||||
y={['FREE_MEMORY', 'USED_MEMORY']}
|
||||
x="TIMESTAMP"
|
||||
enableLegend={true}
|
||||
lineColors={['#039be5', '#757575']}
|
||||
legendNames={[T.FreeMemory, T.UsedMemory]}
|
||||
legendNames={[Tr(T.FreeMemory), Tr(T.UsedMemory)]}
|
||||
interpolationY={(value) => prettyBytes(value)}
|
||||
/>
|
||||
</Grid>
|
||||
|
@ -41,6 +41,7 @@ import {
|
||||
|
||||
import { useGeneralApi } from 'client/features/General'
|
||||
import { T } from 'client/constants'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import {
|
||||
validateResourceId,
|
||||
validateValue,
|
||||
@ -57,6 +58,8 @@ import {
|
||||
} from 'client/components/Tabs/Quota/Components/helpers/subcomponents'
|
||||
import { useGetOneConfigQuery } from 'client/features/OneApi/system'
|
||||
|
||||
import { mapValues, map } from 'lodash'
|
||||
|
||||
/**
|
||||
* QuotaControls Component
|
||||
*
|
||||
@ -107,7 +110,12 @@ export const QuotaControls = memo(
|
||||
return acc
|
||||
}, [])
|
||||
|
||||
const extendedQuotaIdentifiers = { ...quotaIdentifiers }
|
||||
const extendedQuotaIdentifiers = mapValues(quotaIdentifiers, (quotaArray) =>
|
||||
map(quotaArray, (quota) => ({
|
||||
...quota,
|
||||
displayName: Tr(quota.displayName),
|
||||
}))
|
||||
)
|
||||
|
||||
if (!extendedQuotaIdentifiers.VM) {
|
||||
extendedQuotaIdentifiers.VM = []
|
||||
@ -261,11 +269,11 @@ export const QuotaControls = memo(
|
||||
variant="outlined"
|
||||
data-cy="qc-type-selector"
|
||||
>
|
||||
<InputLabel>Type</InputLabel>
|
||||
<InputLabel>{Tr(T.Type)}</InputLabel>
|
||||
<Select
|
||||
value={selectedType || ''}
|
||||
onChange={(e) => setSelectedType(e.target.value)}
|
||||
label="Type"
|
||||
label={Tr(T.Type)}
|
||||
inputProps={{ 'data-cy': 'qc-type-selector-input' }}
|
||||
>
|
||||
{quotaTypes.map((type) => (
|
||||
@ -308,7 +316,7 @@ export const QuotaControls = memo(
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
label="Identifier"
|
||||
label={Tr(T.Identifier)}
|
||||
variant="outlined"
|
||||
inputProps={{
|
||||
...params.inputProps,
|
||||
@ -383,7 +391,7 @@ export const QuotaControls = memo(
|
||||
size={'large'}
|
||||
data-cy={'qc-apply-button'}
|
||||
>
|
||||
{T.Apply}
|
||||
{Tr(T.Apply)}
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item sx={{ mt: 2 }}>
|
||||
@ -392,18 +400,18 @@ export const QuotaControls = memo(
|
||||
color="textSecondary"
|
||||
sx={{ opacity: 0.7 }}
|
||||
>
|
||||
<strong>How to use Quota Controls:</strong>
|
||||
<strong>{Tr(T.QuotaHelpTitle)}:</strong>
|
||||
<ul>
|
||||
<li>Select the quota type from the dropdown.</li>
|
||||
<li>{Tr(T.QuotaHelpStep1)}</li>
|
||||
<Box
|
||||
component="li"
|
||||
sx={{
|
||||
textDecoration: 'underline',
|
||||
fontWeight: 'bold',
|
||||
}}
|
||||
title="enter Resource IDs over which this quota will apply"
|
||||
title={Tr(T.QuotaHelpStep2Tooltip)}
|
||||
>
|
||||
(Optional) Individual Resource Quotas .
|
||||
{Tr(T.QuotaHelpStep2)}
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
@ -412,13 +420,13 @@ export const QuotaControls = memo(
|
||||
textDecoration: 'underline',
|
||||
fontWeight: 'bold',
|
||||
}}
|
||||
title="this further qualifies the quota type to a more specific attribute"
|
||||
title={Tr(T.QuotaHelpStep3Tooltip)}
|
||||
>
|
||||
Select identifiers for the quota.
|
||||
{Tr(T.QuotaHelpStep3)}
|
||||
</Box>
|
||||
|
||||
<li>Enter the value for the selected quota.</li>
|
||||
<li>Click Apply to set the quotas.</li>
|
||||
<li>{Tr(T.QuotaHelpStep4)}</li>
|
||||
<li>{Tr(T.QuotaHelpStep5)}</li>
|
||||
</ul>
|
||||
</Typography>
|
||||
</Grid>
|
||||
|
@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { T } from 'client/constants'
|
||||
|
||||
/**
|
||||
* @param {Array} values - Array of values
|
||||
@ -129,11 +130,9 @@ export const handleApplyGlobalQuotas = async (
|
||||
if (result.error) {
|
||||
throw new Error(result.error.message)
|
||||
}
|
||||
enqueueSuccess(`Quota updated successfully for ID ${actualId}`)
|
||||
enqueueSuccess(T.SuccessQuotaUpdated, [actualId.toString()])
|
||||
} catch (error) {
|
||||
enqueueError(
|
||||
`Error updating quota for ID ${resourceIdOrName}: ${error.message}`
|
||||
)
|
||||
enqueueError(T.ErrorQuotaUpdated, [resourceIdOrName, error.message])
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,7 +146,7 @@ export const handleApplyGlobalQuotas = async (
|
||||
if (value !== undefined && value !== '') {
|
||||
await applyQuotaChange(resourceId, value)
|
||||
} else {
|
||||
enqueueError(`No value specified for Resource ID ${resourceId}`)
|
||||
enqueueError(T.ErrorQuotaNoValueSpecified, resourceId)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -206,18 +205,18 @@ const quotasToXml = (type, resourceId, quota) => {
|
||||
|
||||
export const quotaIdentifiers = {
|
||||
VM: [
|
||||
{ id: 'VMS', displayName: 'Virtual Machines' },
|
||||
{ id: 'RUNNING_VMS', displayName: 'Running VMs' },
|
||||
{ id: 'MEMORY', displayName: 'Memory' },
|
||||
{ id: 'RUNNING_MEMORY', displayName: 'Running Memory' },
|
||||
{ id: 'CPU', displayName: 'CPU' },
|
||||
{ id: 'RUNNING_CPU', displayName: 'Running CPU' },
|
||||
{ id: 'SYSTEM_DISK_SIZE', displayName: 'System Disk Size' },
|
||||
{ id: 'VMS', displayName: T.VirtualMachines },
|
||||
{ id: 'RUNNING_VMS', displayName: T.RunningVMs },
|
||||
{ id: 'MEMORY', displayName: T.Memory },
|
||||
{ id: 'RUNNING_MEMORY', displayName: T.RunningMemory },
|
||||
{ id: 'CPU', displayName: T.CPU },
|
||||
{ id: 'RUNNING_CPU', displayName: T.RunningCPU },
|
||||
{ id: 'SYSTEM_DISK_SIZE', displayName: T.SystemDiskSize },
|
||||
],
|
||||
DATASTORE: [
|
||||
{ id: 'SIZE', displayName: 'Size' },
|
||||
{ id: 'IMAGES', displayName: 'Images' },
|
||||
{ id: 'SIZE', displayName: T.Size },
|
||||
{ id: 'IMAGES', displayName: T.Images },
|
||||
],
|
||||
NETWORK: [{ id: 'LEASES', displayName: 'Leases' }],
|
||||
IMAGE: [{ id: 'RVMS', displayName: 'Running VMs' }],
|
||||
NETWORK: [{ id: 'LEASES', displayName: T.Leases }],
|
||||
IMAGE: [{ id: 'RVMS', displayName: T.RunningVMs }],
|
||||
}
|
||||
|
@ -24,6 +24,8 @@ import {
|
||||
Grid,
|
||||
} from '@mui/material'
|
||||
import { Cancel } from 'iconoir-react'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
/**
|
||||
* @param {object} props - The props for the component.
|
||||
@ -76,7 +78,7 @@ export const HybridInputField = ({
|
||||
return (
|
||||
<>
|
||||
<TextField
|
||||
label="Value"
|
||||
label={Tr(T.Value)}
|
||||
disabled={isDisabled()}
|
||||
value={getValue()}
|
||||
onChange={(e) => {
|
||||
|
@ -17,7 +17,8 @@ import PropTypes from 'prop-types'
|
||||
import { Component, useState } from 'react'
|
||||
import { Autocomplete, Box, Chip, IconButton, TextField } from '@mui/material'
|
||||
import { Trash } from 'iconoir-react'
|
||||
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
/**
|
||||
* @param {object} root0 - Component
|
||||
* @param {string} root0.selectedType - Selected Quota type.
|
||||
@ -119,18 +120,15 @@ export const ResourceIDAutocomplete = ({
|
||||
setInputValue('')
|
||||
}}
|
||||
variant="outlined"
|
||||
label="Resource IDs"
|
||||
label={Tr(T.ResourceIds)}
|
||||
placeholder={
|
||||
inputValue || state?.globalIds?.length > 1
|
||||
? ''
|
||||
: 'Select or type a Resource ID'
|
||||
: Tr(T.ResourceIdsConcept)
|
||||
}
|
||||
fullWidth
|
||||
error={!state.isValid}
|
||||
helperText={
|
||||
!state.isValid &&
|
||||
'Invalid format or duplicate ID. Please enter a positive number.'
|
||||
}
|
||||
helperText={!state.isValid && Tr(T.ResourceIdsInvalid)}
|
||||
sx={{
|
||||
'.MuiOutlinedInput-root': {
|
||||
minHeight: '56px',
|
||||
|
@ -25,7 +25,8 @@ import { nameMapper } from 'client/components/Tabs/Quota/Components/helpers/scri
|
||||
import { useGetDatastoresQuery } from 'client/features/OneApi/datastore'
|
||||
import { useGetVNetworksQuery } from 'client/features/OneApi/network'
|
||||
import { useGetImagesQuery } from 'client/features/OneApi/image'
|
||||
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
/**
|
||||
* Generates a QuotaInfoTab for an user or a group.
|
||||
*
|
||||
@ -105,9 +106,8 @@ const generateQuotasInfoTab = ({ groups }) => {
|
||||
const transformedKey = key
|
||||
.replace(/_/g, ' ')
|
||||
.split(' ')
|
||||
.map(
|
||||
(word) =>
|
||||
word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
|
||||
.map((word) =>
|
||||
Tr(word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
||||
)
|
||||
.join(' ')
|
||||
|
||||
@ -144,7 +144,7 @@ const generateQuotasInfoTab = ({ groups }) => {
|
||||
|
||||
const quotaTypesConfig = [
|
||||
{
|
||||
title: 'VM Quota',
|
||||
title: Tr(T.VMQuota),
|
||||
quota: Array.isArray(apiData.VM_QUOTA)
|
||||
? apiData.VM_QUOTA
|
||||
: [apiData.VM_QUOTA],
|
||||
@ -152,7 +152,7 @@ const generateQuotasInfoTab = ({ groups }) => {
|
||||
keyMap: generateKeyMap(apiData.VM_QUOTA),
|
||||
},
|
||||
{
|
||||
title: 'Datastore Quota',
|
||||
title: Tr(T.DatastoreQuota),
|
||||
quota: Array.isArray(apiData.DATASTORE_QUOTA)
|
||||
? apiData.DATASTORE_QUOTA
|
||||
: [apiData.DATASTORE_QUOTA],
|
||||
@ -160,7 +160,7 @@ const generateQuotasInfoTab = ({ groups }) => {
|
||||
keyMap: generateKeyMap(apiData.DATASTORE_QUOTA),
|
||||
},
|
||||
{
|
||||
title: 'Network Quota',
|
||||
title: Tr(T.NetworkQuota),
|
||||
quota: Array.isArray(apiData.NETWORK_QUOTA)
|
||||
? apiData.NETWORK_QUOTA
|
||||
: [apiData.NETWORK_QUOTA],
|
||||
@ -168,7 +168,7 @@ const generateQuotasInfoTab = ({ groups }) => {
|
||||
keyMap: generateKeyMap(apiData.NETWORK_QUOTA),
|
||||
},
|
||||
{
|
||||
title: 'Image Quota',
|
||||
title: Tr(T.ImageQuota),
|
||||
quota: Array.isArray(apiData.IMAGE_QUOTA)
|
||||
? apiData.IMAGE_QUOTA
|
||||
: [apiData.IMAGE_QUOTA],
|
||||
@ -249,7 +249,7 @@ const generateQuotasInfoTab = ({ groups }) => {
|
||||
textAlign={'center'}
|
||||
sx={{ opacity: 0.8 }}
|
||||
>
|
||||
Quota Controls
|
||||
{Tr(T.QuotaControls)}
|
||||
</Typography>
|
||||
<Box overflow={'auto'}>
|
||||
<QuotaControls
|
||||
|
@ -18,6 +18,7 @@ import { useState, Component } from 'react'
|
||||
import { Box, Button, Menu, MenuItem, IconButton } from '@mui/material'
|
||||
import PropTypes from 'prop-types'
|
||||
import { NavArrowDown } from 'iconoir-react'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
/**
|
||||
* @param {object} root0 - Props
|
||||
@ -68,7 +69,7 @@ export const ButtonGenerator = ({ items, options = {} }) => {
|
||||
endIcon={items.length > 1 ? <NavArrowDown /> : null}
|
||||
{...options?.button}
|
||||
>
|
||||
{options?.button?.title || ''}
|
||||
{options?.button?.title ? Tr(options?.button?.title) : ''}
|
||||
</Button>
|
||||
)}
|
||||
<Menu
|
||||
@ -88,7 +89,7 @@ export const ButtonGenerator = ({ items, options = {} }) => {
|
||||
}}
|
||||
{...options?.menuItem}
|
||||
>
|
||||
{name}
|
||||
{Tr(name)}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
@ -120,7 +121,11 @@ export const ButtonGenerator = ({ items, options = {} }) => {
|
||||
...options?.singleButton?.sx,
|
||||
}}
|
||||
>
|
||||
{options?.singleButton?.title || items.name || ''}
|
||||
{options?.singleButton?.title
|
||||
? Tr(options?.singleButton?.title)
|
||||
: items.name
|
||||
? Tr(items.name)
|
||||
: ''}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
@ -32,7 +32,8 @@ import {
|
||||
} from '@mui/material'
|
||||
import { useGetServiceQuery } from 'client/features/OneApi/service'
|
||||
import { timeFromMilliseconds } from 'client/models/Helper'
|
||||
import { SERVICE_LOG_SEVERITY } from 'client/constants'
|
||||
import { SERVICE_LOG_SEVERITY, T } from 'client/constants'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
const PAGE_SIZE = 10
|
||||
|
||||
@ -125,24 +126,24 @@ const LogTab = ({ id }) => {
|
||||
>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Search"
|
||||
label={Tr(T.Search)}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
onChange={(e) => setFilter(e.target.value)}
|
||||
/>
|
||||
|
||||
<FormControl fullWidth size="small">
|
||||
<InputLabel id="severity-select-label">Severity</InputLabel>
|
||||
<InputLabel id="severity-select-label">{Tr(T.Severity)}</InputLabel>
|
||||
<Select
|
||||
labelId="severity-select-label"
|
||||
label="Severity"
|
||||
label={Tr(T.Severity)}
|
||||
value={severityFilter}
|
||||
onChange={(e) => setSeverityFilter(e.target.value)}
|
||||
>
|
||||
<MenuItem value="ALL">All Severities</MenuItem>
|
||||
<MenuItem value="ALL">{Tr(T.AllSeverities)}</MenuItem>
|
||||
{Object.entries(severityDisplayNames).map(([key, name]) => (
|
||||
<MenuItem key={key} value={key}>
|
||||
{name}
|
||||
{Tr(T[name])}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
@ -154,7 +155,7 @@ const LogTab = ({ id }) => {
|
||||
onClick={() => handleSortChange(key)}
|
||||
variant={sortType === key ? 'contained' : 'outlined'}
|
||||
>
|
||||
{name}
|
||||
{Tr(T[name])}
|
||||
</Button>
|
||||
))}
|
||||
</ButtonGroup>
|
||||
@ -165,7 +166,7 @@ const LogTab = ({ id }) => {
|
||||
onChange={() => setSortAscending(!sortAscending)}
|
||||
/>
|
||||
}
|
||||
label={sortAscending ? 'Asc' : 'Desc'}
|
||||
label={sortAscending ? Tr(T.Asc) : Tr(T.Desc)}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
|
@ -42,6 +42,8 @@ import {
|
||||
} from 'iconoir-react'
|
||||
|
||||
import { useGeneralApi } from 'client/features/General'
|
||||
import { T } from 'client/constants'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
// Filters actions based on the data-cy key
|
||||
const filterActions = ['vm_resume', 'vm-manage', 'vm-host', 'vm-terminate']
|
||||
@ -74,18 +76,16 @@ const RolesTab = ({ id }) => {
|
||||
const roleName = roles?.[roleIdx]?.name
|
||||
|
||||
try {
|
||||
enqueueInfo(`Starting '${actionType}' action on role: ${roleName}`)
|
||||
enqueueInfo(T.InfoServiceActionRole, [actionType, roleName])
|
||||
|
||||
await createApiCallback(addRoleAction)({
|
||||
perform: actionType,
|
||||
role: roleName,
|
||||
})
|
||||
|
||||
enqueueSuccess(`Action '${actionType}' completed on role: ${roleName}`)
|
||||
enqueueSuccess(T.SuccessRoleActionCompleted, [actionType, roleName])
|
||||
} catch (error) {
|
||||
enqueueError(
|
||||
`Action '${actionType}' failed on role: ${roleName}. Error: ${error}`
|
||||
)
|
||||
enqueueError(T.ErrorServiceActionRole, [actionType, roleName, error])
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -186,7 +186,7 @@ const RolesTab = ({ id }) => {
|
||||
<>
|
||||
<ButtonGenerator
|
||||
items={{
|
||||
name: 'Add Role',
|
||||
name: T.AddRole,
|
||||
onClick: handleOpenAddRole,
|
||||
icon: Plus,
|
||||
}}
|
||||
@ -209,7 +209,7 @@ const RolesTab = ({ id }) => {
|
||||
|
||||
<ButtonGenerator
|
||||
items={{
|
||||
name: 'Scale',
|
||||
name: T.Scale,
|
||||
onClick: handleOpenScale,
|
||||
icon: Plus,
|
||||
}}
|
||||
@ -255,7 +255,7 @@ const RolesTab = ({ id }) => {
|
||||
<ButtonGenerator
|
||||
items={[
|
||||
{
|
||||
name: 'Suspend',
|
||||
name: T.Suspend,
|
||||
onClick: () =>
|
||||
handleAddRoleAction({
|
||||
perform: 'suspend',
|
||||
@ -263,13 +263,11 @@ const RolesTab = ({ id }) => {
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'Poweroff',
|
||||
|
||||
name: T.Poweroff,
|
||||
onClick: () => handleAddRoleAction('poweroff'),
|
||||
},
|
||||
{
|
||||
name: 'Poweroff Hard',
|
||||
|
||||
name: T.PoweroffHard,
|
||||
onClick: () => handleAddRoleAction('poweroff-hard'),
|
||||
},
|
||||
]}
|
||||
@ -290,16 +288,15 @@ const RolesTab = ({ id }) => {
|
||||
<ButtonGenerator
|
||||
items={[
|
||||
{
|
||||
name: 'Stop',
|
||||
|
||||
name: T.Stop,
|
||||
onClick: () => handleAddRoleAction('stop'),
|
||||
},
|
||||
{
|
||||
name: 'Undeploy',
|
||||
name: T.Undeploy,
|
||||
onClick: () => handleAddRoleAction('undeploy'),
|
||||
},
|
||||
{
|
||||
name: 'Undeploy Hard',
|
||||
name: T.UndeployHard,
|
||||
onClick: () => handleAddRoleAction('undeploy-hard'),
|
||||
},
|
||||
]}
|
||||
@ -320,11 +317,11 @@ const RolesTab = ({ id }) => {
|
||||
<ButtonGenerator
|
||||
items={[
|
||||
{
|
||||
name: 'Reboot',
|
||||
name: T.Reboot,
|
||||
onClick: () => handleAddRoleAction('reboot'),
|
||||
},
|
||||
{
|
||||
name: 'Reboot Hard',
|
||||
name: T.RebootHard,
|
||||
onClick: () => handleAddRoleAction('reboot-hard'),
|
||||
},
|
||||
]}
|
||||
@ -346,11 +343,11 @@ const RolesTab = ({ id }) => {
|
||||
<ButtonGenerator
|
||||
items={[
|
||||
{
|
||||
name: 'Terminate',
|
||||
name: T.Terminate,
|
||||
onClick: () => handleAddRoleAction('terminate'),
|
||||
},
|
||||
{
|
||||
name: 'Terminate Hard',
|
||||
name: T.TerminateHard,
|
||||
onClick: () => handleAddRoleAction('terminate-hard'),
|
||||
},
|
||||
]}
|
||||
@ -458,9 +455,11 @@ const RoleComponent = memo(({ role, selected, status }) => {
|
||||
{name}
|
||||
</Typography>
|
||||
<Typography variant="body1" mb={1}>
|
||||
VM Template ID: {templateId}
|
||||
{Tr(T.VMTemplate)} {Tr(T.ID)}: {templateId}
|
||||
</Typography>
|
||||
<Typography variant="body1">
|
||||
{Tr(T.Cardinality)}: {cardinality}
|
||||
</Typography>
|
||||
<Typography variant="body1">Cardinality: {cardinality}</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
|
@ -25,6 +25,8 @@ import {
|
||||
Box,
|
||||
Typography,
|
||||
} from '@mui/material'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
/**
|
||||
* Dialog for scaling the number of VMs.
|
||||
@ -67,7 +69,7 @@ export const ScaleDialog = ({ open, onClose, onScale, roleName }) => {
|
||||
<TextField
|
||||
margin="normal"
|
||||
fullWidth
|
||||
label="Number of VMs"
|
||||
label={Tr(T.NumberOfVms)}
|
||||
type="number"
|
||||
{...register('numberOfVms', {
|
||||
required: 'Number of VMs is required',
|
||||
@ -77,7 +79,7 @@ export const ScaleDialog = ({ open, onClose, onScale, roleName }) => {
|
||||
/>
|
||||
<FormControlLabel
|
||||
control={<Switch {...register('force')} />}
|
||||
label="Force"
|
||||
label={Tr(T.Force)}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
@ -86,7 +88,7 @@ export const ScaleDialog = ({ open, onClose, onScale, roleName }) => {
|
||||
color="primary"
|
||||
sx={{ mt: 2, fontSize: '1rem' }}
|
||||
>
|
||||
Scale
|
||||
{Tr(T.Scale)}
|
||||
</Button>
|
||||
</form>
|
||||
</Box>
|
||||
|
@ -25,6 +25,7 @@ import { DateTime } from 'luxon'
|
||||
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
import { mapValues } from 'lodash'
|
||||
|
||||
const keyMap = {
|
||||
VMID: 'OID',
|
||||
@ -195,6 +196,13 @@ const generateShowbackInfoTab = ({ groups }) => {
|
||||
)
|
||||
}
|
||||
|
||||
const topMetricNamesTranslated = mapValues(topMetricNames, (value, key) =>
|
||||
Tr(value)
|
||||
)
|
||||
const metricNamesTranslated = mapValues(metricNames, (value, key) =>
|
||||
Tr(value)
|
||||
)
|
||||
|
||||
return (
|
||||
<Box padding={2} display="flex" flexDirection="column" height="100%">
|
||||
<Box
|
||||
@ -230,7 +238,7 @@ const generateShowbackInfoTab = ({ groups }) => {
|
||||
chartType={'table'}
|
||||
tableColumns={smallTableColumns}
|
||||
groupBy={'MONTH'}
|
||||
metricNames={topMetricNames}
|
||||
metricNames={topMetricNamesTranslated}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@ -240,7 +248,7 @@ const generateShowbackInfoTab = ({ groups }) => {
|
||||
chartType={'bar'}
|
||||
ItemsPerPage={12}
|
||||
groupBy={'MONTH'}
|
||||
metricNames={topMetricNames}
|
||||
metricNames={topMetricNamesTranslated}
|
||||
selectedMetrics={{ totalCost: true }}
|
||||
/>
|
||||
</Box>
|
||||
@ -258,7 +266,7 @@ const generateShowbackInfoTab = ({ groups }) => {
|
||||
ItemsPerPage={7}
|
||||
tableColumns={DataGridColumns}
|
||||
groupBy={'MONTH'}
|
||||
metricNames={metricNames}
|
||||
metricNames={metricNamesTranslated}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
|
@ -14,8 +14,6 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import {
|
||||
// Switch,
|
||||
// FormControlLabel,
|
||||
Box,
|
||||
Button,
|
||||
Dialog,
|
||||
@ -37,7 +35,8 @@ import {
|
||||
import { EditPencil } from 'iconoir-react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Component, useCallback, useReducer } from 'react'
|
||||
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
/**
|
||||
* Reducer to manage the state of the AuthenticationInfo component.
|
||||
*
|
||||
@ -107,15 +106,15 @@ const AuthenticationInfo = ({ id }) => {
|
||||
await changeAuthDriver(updateParams)
|
||||
|
||||
if (passwordUpdated) {
|
||||
enqueueSuccess('Password updated successfully!')
|
||||
enqueueSuccess(T.SuccessPasswordUpdated)
|
||||
} else {
|
||||
enqueueSuccess('Authentication driver updated successfully!')
|
||||
enqueueSuccess(T.SuccessAuthDriver)
|
||||
}
|
||||
} catch (error) {
|
||||
if (passwordUpdated) {
|
||||
enqueueError(`Error updating password: ${error.message}`)
|
||||
enqueueError(T.ErrorPasswordUpdated, error.message)
|
||||
} else {
|
||||
enqueueError(`Error updating authentication driver: ${error.message}`)
|
||||
enqueueError(T.ErrorAuthDriverUpdated, error.message)
|
||||
}
|
||||
}
|
||||
}, [id, state, user.PASSWORD, changeAuthDriver, enqueueSuccess, enqueueError])
|
||||
@ -135,9 +134,9 @@ const AuthenticationInfo = ({ id }) => {
|
||||
})
|
||||
|
||||
handleFieldChange('sshKeyDialogOpen', false)
|
||||
enqueueSuccess('Public SSH Key updated successfully!')
|
||||
enqueueSuccess(T.SuccessPublicSSHKeyUpdated)
|
||||
} catch (error) {
|
||||
enqueueError(`Error updating Public SSH Key: ${error.message}`)
|
||||
enqueueError(T.ErrorPublicSSHKeyUpdated, error.message)
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,9 +151,9 @@ const AuthenticationInfo = ({ id }) => {
|
||||
})
|
||||
|
||||
handleFieldChange('privateSshKeyDialogOpen', false)
|
||||
enqueueSuccess('Private SSH Key updated successfully!')
|
||||
enqueueSuccess(T.SuccessPrivateSSHKeyUpdated)
|
||||
} catch (error) {
|
||||
enqueueError(`Error updating Private SSH Key: ${error.message}`)
|
||||
enqueueError(T.ErrorPrivateSSHKeyUpdated, error.message)
|
||||
}
|
||||
}
|
||||
|
||||
@ -169,9 +168,9 @@ const AuthenticationInfo = ({ id }) => {
|
||||
})
|
||||
|
||||
handleFieldChange('passphraseDialogOpen', false)
|
||||
enqueueSuccess('SSH Key Passphrase updated successfully!')
|
||||
enqueueSuccess(T.SuccessPassphraseSSHKeyUpdated)
|
||||
} catch (error) {
|
||||
enqueueError(`Error updating SSH Key Passphrase: ${error.message}`)
|
||||
enqueueError(T.ErrorPassphraseSSHKeyUpdated, error.message)
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,12 +187,12 @@ const AuthenticationInfo = ({ id }) => {
|
||||
minHeight={'300px'}
|
||||
>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Authentication
|
||||
{Tr(T.Authentication)}
|
||||
</Typography>
|
||||
<Grid container spacing={2} style={{ flexGrow: 1 }}>
|
||||
<Grid item xs={6}>
|
||||
<TextField
|
||||
label="Authentication driver"
|
||||
label={Tr(T.AuthenticationDriver)}
|
||||
value={state.authDriver}
|
||||
onChange={(e) => handleFieldChange('authDriver', e.target.value)}
|
||||
fullWidth
|
||||
@ -229,7 +228,7 @@ const AuthenticationInfo = ({ id }) => {
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<TextField
|
||||
label="Password"
|
||||
label={Tr(T.Password)}
|
||||
value={state.password}
|
||||
onChange={(e) => handleFieldChange('password', e.target.value)}
|
||||
fullWidth
|
||||
@ -247,7 +246,7 @@ const AuthenticationInfo = ({ id }) => {
|
||||
>
|
||||
<EditPencil />
|
||||
</IconButton>
|
||||
<Typography display="inline">Edit Public SSH Key</Typography>
|
||||
<Typography display="inline">{Tr(T.EditPublicSSHKey)}</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<IconButton
|
||||
@ -255,7 +254,7 @@ const AuthenticationInfo = ({ id }) => {
|
||||
>
|
||||
<EditPencil />
|
||||
</IconButton>
|
||||
<Typography display="inline">Edit Private SSH Key</Typography>
|
||||
<Typography display="inline">{Tr(T.EditPrivateSSHKey)}</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<IconButton
|
||||
@ -263,7 +262,9 @@ const AuthenticationInfo = ({ id }) => {
|
||||
>
|
||||
<EditPencil />
|
||||
</IconButton>
|
||||
<Typography display="inline">Edit SSH Key Passphrase</Typography>
|
||||
<Typography display="inline">
|
||||
{Tr(T.EditSSHKeyPassphrase)}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Button
|
||||
@ -272,7 +273,7 @@ const AuthenticationInfo = ({ id }) => {
|
||||
onClick={handleUpdateAuthDriver}
|
||||
data-cy={'auth-save'}
|
||||
>
|
||||
Save Changes
|
||||
{Tr(T.SaveChanges)}
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@ -280,7 +281,7 @@ const AuthenticationInfo = ({ id }) => {
|
||||
open={state.sshKeyDialogOpen}
|
||||
onClose={() => handleFieldChange('sshKeyDialogOpen', false)}
|
||||
>
|
||||
<DialogTitle>Edit Public SSH Key</DialogTitle>
|
||||
<DialogTitle>{Tr(T.EditPublicSSHKey)}</DialogTitle>
|
||||
<DialogContent>
|
||||
<TextField
|
||||
fullWidth
|
||||
@ -288,7 +289,7 @@ const AuthenticationInfo = ({ id }) => {
|
||||
rows={4}
|
||||
value={state.sshKey}
|
||||
onChange={(e) => handleFieldChange('sshKey', e.target.value)}
|
||||
placeholder="Paste your SSH Key here..."
|
||||
placeholder={Tr(T.PasteSSHKey)}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
@ -296,10 +297,10 @@ const AuthenticationInfo = ({ id }) => {
|
||||
onClick={() => handleFieldChange('sshKeyDialogOpen', false)}
|
||||
color="primary"
|
||||
>
|
||||
Cancel
|
||||
{Tr(T.Cancel)}
|
||||
</Button>
|
||||
<Button onClick={handleSshKeySave} color="primary">
|
||||
Save
|
||||
{Tr(T.Save)}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
@ -307,7 +308,7 @@ const AuthenticationInfo = ({ id }) => {
|
||||
open={state.privateSshKeyDialogOpen}
|
||||
onClose={() => handleFieldChange('privateSshKeyDialogOpen', false)}
|
||||
>
|
||||
<DialogTitle>Edit Private SSH Key</DialogTitle>
|
||||
<DialogTitle>{Tr(T.EditPrivateSSHKey)}</DialogTitle>
|
||||
<DialogContent>
|
||||
<TextField
|
||||
fullWidth
|
||||
@ -317,7 +318,7 @@ const AuthenticationInfo = ({ id }) => {
|
||||
onChange={(e) =>
|
||||
handleFieldChange('privateSshKey', e.target.value)
|
||||
}
|
||||
placeholder="Paste your Private SSH Key here..."
|
||||
placeholder={Tr(T.PastePrivateSSHKey)}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
@ -327,10 +328,10 @@ const AuthenticationInfo = ({ id }) => {
|
||||
}
|
||||
color="primary"
|
||||
>
|
||||
Cancel
|
||||
{Tr(T.Cancel)}
|
||||
</Button>
|
||||
<Button onClick={handlePrivateSshKeySave} color="primary">
|
||||
Save
|
||||
{Tr(T.Save)}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
@ -339,13 +340,13 @@ const AuthenticationInfo = ({ id }) => {
|
||||
open={state.passphraseDialogOpen}
|
||||
onClose={() => handleFieldChange('passphraseDialogOpen', false)}
|
||||
>
|
||||
<DialogTitle>Edit SSH Key Passphrase</DialogTitle>
|
||||
<DialogTitle>{Tr(T.EditSSHKeyPassphrase)}</DialogTitle>
|
||||
<DialogContent>
|
||||
<TextField
|
||||
fullWidth
|
||||
value={state.passphrase}
|
||||
onChange={(e) => handleFieldChange('passphrase', e.target.value)}
|
||||
placeholder="Enter your passphrase..."
|
||||
placeholder={Tr(T.EnterPassphrase)}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
@ -353,10 +354,10 @@ const AuthenticationInfo = ({ id }) => {
|
||||
onClick={() => handleFieldChange('passphraseDialogOpen', false)}
|
||||
color="primary"
|
||||
>
|
||||
Cancel
|
||||
{Tr(T.Cancel)}
|
||||
</Button>
|
||||
<Button onClick={handlePassphraseSave} color="primary">
|
||||
Save
|
||||
{Tr(T.Save)}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
@ -26,6 +26,7 @@ import { useGetGroupsQuery } from 'client/features/OneApi/group'
|
||||
import { Chip, Box, Grid, Typography } from '@mui/material'
|
||||
|
||||
import { T } from 'client/constants'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
/**
|
||||
* Renders mainly information tab.
|
||||
@ -70,7 +71,7 @@ const GroupsInfoTab = ({ id }) => {
|
||||
<Grid container spacing={2} alignItems="center">
|
||||
{primaryGroupName && (
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h7">{T.Primary}</Typography>
|
||||
<Typography variant="h7">{Tr(T.Primary)}</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
@ -90,7 +91,7 @@ const GroupsInfoTab = ({ id }) => {
|
||||
|
||||
{secondaryGroupNames.length > 0 && (
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body2">{T.Secondary}</Typography>
|
||||
<Typography variant="body2">{Tr(T.Secondary)}</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
|
@ -14,6 +14,8 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
import { T } from 'client/constants'
|
||||
|
||||
/**
|
||||
* @param {Array} values - Array of values
|
||||
* @param {Array} globalIds - Global ids array
|
||||
@ -129,11 +131,9 @@ export const handleApplyGlobalQuotas = async (
|
||||
if (result.error) {
|
||||
throw new Error(result.error.message)
|
||||
}
|
||||
enqueueSuccess(`Quota updated successfully for ID ${actualId}`)
|
||||
enqueueSuccess(T.SuccessQuotaUpdated, actualId)
|
||||
} catch (error) {
|
||||
enqueueError(
|
||||
`Error updating quota for ID ${resourceIdOrName}: ${error.message}`
|
||||
)
|
||||
enqueueError(T.ErrorQuotaUpdated, [resourceIdOrName, error.message])
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,7 +147,7 @@ export const handleApplyGlobalQuotas = async (
|
||||
if (value !== undefined && value !== '') {
|
||||
await applyQuotaChange(resourceId, value)
|
||||
} else {
|
||||
enqueueError(`No value specified for Resource ID ${resourceId}`)
|
||||
enqueueError(T.ErrorQuotaNoValueSpecified, resourceId)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -206,18 +206,18 @@ const quotasToXml = (type, resourceId, quota) => {
|
||||
|
||||
export const quotaIdentifiers = {
|
||||
VM: [
|
||||
{ id: 'VMS', displayName: 'Virtual Machines' },
|
||||
{ id: 'RUNNING_VMS', displayName: 'Running VMs' },
|
||||
{ id: 'MEMORY', displayName: 'Memory' },
|
||||
{ id: 'RUNNING_MEMORY', displayName: 'Running Memory' },
|
||||
{ id: 'CPU', displayName: 'CPU' },
|
||||
{ id: 'RUNNING_CPU', displayName: 'Running CPU' },
|
||||
{ id: 'SYSTEM_DISK_SIZE', displayName: 'System Disk Size' },
|
||||
{ id: 'VMS', displayName: T.VirtualMachines },
|
||||
{ id: 'RUNNING_VMS', displayName: T.RunningVMs },
|
||||
{ id: 'MEMORY', displayName: T.Memory },
|
||||
{ id: 'RUNNING_MEMORY', displayName: T.RunningMemory },
|
||||
{ id: 'CPU', displayName: T.CPU },
|
||||
{ id: 'RUNNING_CPU', displayName: T.RunningCPU },
|
||||
{ id: 'SYSTEM_DISK_SIZE', displayName: T.SystemDiskSize },
|
||||
],
|
||||
DATASTORE: [
|
||||
{ id: 'SIZE', displayName: 'Size' },
|
||||
{ id: 'IMAGES', displayName: 'Images' },
|
||||
{ id: 'SIZE', displayName: T.Size },
|
||||
{ id: 'IMAGES', displayName: T.Images },
|
||||
],
|
||||
NETWORK: [{ id: 'LEASES', displayName: 'Leases' }],
|
||||
IMAGE: [{ id: 'RVMS', displayName: 'Running VMs' }],
|
||||
NETWORK: [{ id: 'LEASES', displayName: T.Leases }],
|
||||
IMAGE: [{ id: 'RVMS', displayName: T.RunningVMs }],
|
||||
}
|
||||
|
@ -24,6 +24,8 @@ import {
|
||||
Grid,
|
||||
} from '@mui/material'
|
||||
import { Cancel } from 'iconoir-react'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
/**
|
||||
* @param {object} props - The props for the component.
|
||||
@ -76,7 +78,7 @@ export const HybridInputField = ({
|
||||
return (
|
||||
<>
|
||||
<TextField
|
||||
label="Value"
|
||||
label={Tr(T.VAlue)}
|
||||
disabled={isDisabled()}
|
||||
value={getValue()}
|
||||
onChange={(e) => {
|
||||
|
@ -36,8 +36,8 @@ import {
|
||||
import { useGeneralApi } from 'client/features/General'
|
||||
|
||||
const LEASES_COLUMNS = [
|
||||
'RESOURCE',
|
||||
'STATE',
|
||||
T.Resource,
|
||||
T.State,
|
||||
'IP',
|
||||
'IP6',
|
||||
'MAC',
|
||||
@ -137,7 +137,7 @@ const LeasesTab = ({ tabProps: { actions } = {}, id }) => {
|
||||
isSubmitting={isHolding}
|
||||
color="secondary"
|
||||
variant="outlined"
|
||||
label={'Hold IP'}
|
||||
label={T.HoldIP}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
|
@ -96,7 +96,7 @@ const VmSnapshotTab = ({ tabProps: { actions } = {}, id }) => {
|
||||
opacity: 0.8,
|
||||
}}
|
||||
>
|
||||
Taking snapshots is not available.
|
||||
{Tr(T.VmSnapshotNotAvailable)}
|
||||
</Typography>
|
||||
<List>
|
||||
<ListItem>
|
||||
@ -120,8 +120,7 @@ const VmSnapshotTab = ({ tabProps: { actions } = {}, id }) => {
|
||||
marginTop: '1em',
|
||||
}}
|
||||
>
|
||||
If none of the above worked, please refer to the VM monitoring
|
||||
logs.
|
||||
{Tr(T.VmSnapshotReferLogs)}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
@ -25,7 +25,7 @@ import { useGetVMGroupQuery } from 'client/features/OneApi/vmGroup'
|
||||
import { Box } from '@mui/material'
|
||||
import { Component } from 'react'
|
||||
import { useGeneralApi } from 'client/features/General'
|
||||
import { VM_STATES } from 'client/constants'
|
||||
import { VM_STATES, T } from 'client/constants'
|
||||
const keyMap = {
|
||||
ID: 'ID',
|
||||
NAME: 'NAME',
|
||||
@ -73,8 +73,7 @@ const VmsInfoTab = ({ id }) => {
|
||||
),
|
||||
]
|
||||
|
||||
includedVms?.isError &&
|
||||
enqueueError('Failed to fetch vm groups, displaying all VMs')
|
||||
includedVms?.isError && enqueueError(T.ErrorVmGroupsFetch)
|
||||
const startMonth = -2
|
||||
const startYear = -2
|
||||
const endMonth = -2
|
||||
|
@ -25,6 +25,7 @@ import {
|
||||
styled,
|
||||
} from '@mui/material'
|
||||
import { WarningCircledOutline } from 'iconoir-react'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
const WarningIcon = styled(WarningCircledOutline)(({ theme }) => ({
|
||||
color: theme.palette.error.main,
|
||||
@ -110,7 +111,7 @@ const Tabs = ({
|
||||
iconPosition="start"
|
||||
icon={error ? <WarningIcon /> : Icon && <Icon />}
|
||||
value={value ?? idx}
|
||||
label={label ?? id}
|
||||
label={Tr(label) ?? id}
|
||||
data-cy={`tab-${id}`}
|
||||
/>
|
||||
)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user