1
0
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:
David 2024-06-20 15:44:24 +02:00 committed by GitHub
parent 3b3fe1ce40
commit 3a691b7a98
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
138 changed files with 1169 additions and 648 deletions

View File

@ -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>

View File

@ -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}`,

View File

@ -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} />

View File

@ -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>

View File

@ -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

View File

@ -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>

View File

@ -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}`}>

View File

@ -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>

View File

@ -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"

View File

@ -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),

View File

@ -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}

View File

@ -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 })
}

View File

@ -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])

View File

@ -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>
)

View File

@ -151,7 +151,7 @@ const SelectController = memo(
>
{values?.map(({ text, value = '' }) => (
<option key={`${name}-${value}`} value={value}>
{text}
{Tr(text)}
</option>
))}
</TextField>

View File

@ -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>
)

View File

@ -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) =>

View File

@ -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>

View File

@ -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 },

View File

@ -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()

View File

@ -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 />

View File

@ -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,

View File

@ -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 (

View File

@ -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) &&

View File

@ -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 (

View File

@ -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

View File

@ -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), {

View File

@ -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(' | ')}
/>

View File

@ -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(),

View File

@ -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()

View File

@ -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(' | ')}
/>

View File

@ -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()

View File

@ -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

View File

@ -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,

View File

@ -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>

View File

@ -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

View File

@ -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"

View File

@ -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>
)

View File

@ -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

View File

@ -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: {

View File

@ -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>

View File

@ -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>
)

View File

@ -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 },

View File

@ -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&apos;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}

View File

@ -31,7 +31,7 @@ const TEMPLATEID = {
preserveState: true,
},
validation: mixed()
.required('VR template ID missing or malformed!')
.required()
.default(() => null),
grid: { md: 12 },
}

View File

@ -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) => (

View File

@ -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>
)}

View File

@ -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"

View File

@ -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>
)

View File

@ -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 }) =>

View File

@ -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>

View File

@ -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

View File

@ -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>
)

View File

@ -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&apos;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>

View File

@ -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>
))}

View File

@ -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>

View File

@ -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)
})
}

View File

@ -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()

View File

@ -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}</>
})

View File

@ -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,

View File

@ -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
}

View File

@ -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) }),

View File

@ -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 ? (

View File

@ -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}

View File

@ -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()
}

View File

@ -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>

View File

@ -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>

View File

@ -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>
)

View File

@ -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,

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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,

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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 && (

View File

@ -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}
/>
)

View File

@ -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>

View File

@ -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>

View File

@ -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 }],
}

View File

@ -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) => {

View File

@ -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',

View File

@ -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

View File

@ -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>
)
}

View File

@ -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>

View File

@ -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>
)

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>
)}

View File

@ -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 }],
}

View File

@ -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) => {

View File

@ -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>
)}

View File

@ -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>

View File

@ -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

View File

@ -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