diff --git a/src/fireedge/src/client/components/Forms/Backup/RestoreForm/Steps/BasicConfiguration/schema.js b/src/fireedge/src/client/components/Forms/Backup/RestoreForm/Steps/BasicConfiguration/schema.js index 32a1821a405..f1f5400c50b 100644 --- a/src/fireedge/src/client/components/Forms/Backup/RestoreForm/Steps/BasicConfiguration/schema.js +++ b/src/fireedge/src/client/components/Forms/Backup/RestoreForm/Steps/BasicConfiguration/schema.js @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { boolean, object, ObjectSchema } from 'yup' -import { Field, getValidationFromFields } from 'client/utils' -import { T, INPUT_TYPES } from 'client/constants' +import { INPUT_TYPES, T } from 'client/constants' +import { timeFromMilliseconds } from 'client/models/Helper' +import { Field, arrayToOptions, getValidationFromFields } from 'client/utils' +import { ObjectSchema, boolean, object, string } from 'yup' const NO_NIC = { name: 'no_nic', @@ -33,14 +34,41 @@ const NO_IP = { grid: { xs: 12, md: 6 }, } +const NAME = { + name: 'name', + label: T.Name, + type: INPUT_TYPES.TEXT, + validation: string(), + grid: { xs: 12, md: 6 }, +} + +const INCREMENT_ID = ({ increments = [] }) => ({ + name: 'increment_id', + label: T.IncrementId, + type: INPUT_TYPES.SELECT, + values: arrayToOptions(increments, { + addEmpty: true, + getText: (increment) => + `${increment.id}: ${timeFromMilliseconds(increment.date) + .toFormat('ff') + .replace(',', '')} (${increment.source})`, + getValue: (increment) => increment.id, + }), + validation: string(), + grid: { xs: 12, md: 6 }, + fieldProps: { + disabled: increments.length === 0, + }, +}) + /** + * @param {object} [data] - Backup data * @returns {Field[]} Fields */ -export const FIELDS = () => [NO_NIC, NO_IP] +export const FIELDS = (data = {}) => [NAME, INCREMENT_ID(data), NO_NIC, NO_IP] /** - * @param {object} [stepProps] - Step props + * @param {object} [data] - Backup data * @returns {ObjectSchema} Schema */ -export const SCHEMA = (stepProps) => - object(getValidationFromFields(FIELDS(stepProps))) +export const SCHEMA = (data) => object(getValidationFromFields(FIELDS(data))) diff --git a/src/fireedge/src/client/components/Forms/Backup/RestoreForm/index.js b/src/fireedge/src/client/components/Forms/Backup/RestoreForm/index.js index 65c3e079518..6a19758925b 100644 --- a/src/fireedge/src/client/components/Forms/Backup/RestoreForm/index.js +++ b/src/fireedge/src/client/components/Forms/Backup/RestoreForm/index.js @@ -21,21 +21,32 @@ import DatastoresTable, { } from 'client/components/Forms/Backup/RestoreForm/Steps/DatastoresTable' import { createSteps } from 'client/utils' -const Steps = createSteps( - (app) => [BasicConfiguration, DatastoresTable].filter(Boolean), - { - transformInitialValue: (app, schema) => - schema.cast({}, { context: { app } }), - transformBeforeSubmit: (formData) => { - const { [BASIC_ID]: configuration, [DATASTORE_ID]: [datastore] = [] } = - formData +const Steps = createSteps([BasicConfiguration, DatastoresTable], { + transformInitialValue: (increments, schema) => { + const castedValuesBasic = schema.cast( + { [BASIC_ID]: { increments } }, + { stripUnknown: true } + ) - return { - datastore: datastore?.ID, - ...configuration, - } - }, - } -) + const castedValuesDatastore = schema.cast( + { [DATASTORE_ID]: {} }, + { stripUnknown: true } + ) + + return { + [BASIC_ID]: castedValuesBasic[BASIC_ID], + [DATASTORE_ID]: castedValuesDatastore[DATASTORE_ID], + } + }, + transformBeforeSubmit: (formData) => { + const { [BASIC_ID]: configuration, [DATASTORE_ID]: [datastore] = [] } = + formData + + return { + datastore: datastore?.ID, + ...configuration, + } + }, +}) export default Steps diff --git a/src/fireedge/src/client/components/Tables/Backups/actions.js b/src/fireedge/src/client/components/Tables/Backups/actions.js index 4ec57947fb7..758958bc975 100644 --- a/src/fireedge/src/client/components/Tables/Backups/actions.js +++ b/src/fireedge/src/client/components/Tables/Backups/actions.js @@ -24,11 +24,11 @@ import { useRestoreBackupMutation, } from 'client/features/OneApi/image' -import { ChangeGroupForm, ChangeUserForm } from 'client/components/Forms/Vm' import { RestoreForm } from 'client/components/Forms/Backup' +import { ChangeGroupForm, ChangeUserForm } from 'client/components/Forms/Vm' import { - createActions, GlobalAction, + createActions, } from 'client/components/Tables/Enhanced/Utils' import { Translate } from 'client/components/HOC' @@ -67,7 +67,7 @@ const MessageToConfirmAction = (rows) => ( ) /** - * Generates the actions to operate resources on Image table. + * Generates the actions to operate resources on Backup table. * * @returns {GlobalAction} - Actions */ @@ -79,7 +79,7 @@ const Actions = () => { const resourcesView = getResourceView(RESOURCE_NAMES.BACKUP)?.actions - const imageActions = useMemo( + const backupActions = useMemo( () => createActions({ filters: resourcesView, @@ -90,22 +90,42 @@ const Actions = () => { dataCy: `image-${IMAGE_ACTIONS.RESTORE}`, label: T.Restore, tooltip: T.Restore, - selected: { min: 1 }, + selected: { max: 1 }, options: [ { dialogProps: { - title: T.SelectCluster, + title: T.RestoreBackup, dataCy: 'modal-select-cluster', }, - form: (rows) => RestoreForm(), + form: (row) => { + const backup = row?.[0]?.original + let increments = backup?.BACKUP_INCREMENTS?.INCREMENT || [] + increments = Array.isArray(increments) + ? increments + : [increments] + increments = increments.map((increment) => ({ + id: increment.ID, + date: increment.DATE, + source: increment.SOURCE, + })) + + return RestoreForm({ + stepProps: { increments }, + initialValues: increments, + }) + }, onSubmit: (rows) => async (formData) => { const ids = rows?.map?.(({ original }) => original?.ID) + let options = `NO_IP="${formData.no_ip}"\nNO_NIC="${formData.no_nic}"\n` + if (formData.name) options += `NAME="${formData.name}"\n` + if (formData.increment_id !== '') + options += `INCREMENT_ID="${formData.increment_id}"\n` await Promise.all( ids.map((id) => restoreBackup({ id: id, datastore: formData.datastore, - options: `NO_IP="${formData.no_ip}"\nNO_NIC="${formData.no_nic}"`, + options, }) ) ) @@ -183,7 +203,7 @@ const Actions = () => { [view] ) - return imageActions + return backupActions } export default Actions diff --git a/src/fireedge/src/client/constants/translates.js b/src/fireedge/src/client/constants/translates.js index 0e5e709fc6d..0ee74a805f6 100644 --- a/src/fireedge/src/client/constants/translates.js +++ b/src/fireedge/src/client/constants/translates.js @@ -413,6 +413,8 @@ module.exports = { Incremental: 'Incremental', Mode: 'Mode', ResetBackup: 'Reset', + IncrementId: 'Increment ID', + RestoreBackup: 'Restore backup', /* sections - templates & instances */ Instances: 'Instances', diff --git a/src/fireedge/src/client/containers/Backups/index.js b/src/fireedge/src/client/containers/Backups/index.js index e6ec253a4f5..64af077c27e 100644 --- a/src/fireedge/src/client/containers/Backups/index.js +++ b/src/fireedge/src/client/containers/Backups/index.js @@ -13,23 +13,23 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { ReactElement, useState, memo } from 'react' -import PropTypes from 'prop-types' +import { Box, Chip, Stack, Typography } from '@mui/material' +import Cancel from 'iconoir-react/dist/Cancel' import GotoIcon from 'iconoir-react/dist/Pin' import RefreshDouble from 'iconoir-react/dist/RefreshDouble' -import Cancel from 'iconoir-react/dist/Cancel' -import { Typography, Box, Stack, Chip } from '@mui/material' +import PropTypes from 'prop-types' +import { ReactElement, memo, useState } from 'react' import { Row } from 'react-table' -import { useLazyGetImageQuery } from 'client/features/OneApi/image' +import { SubmitButton } from 'client/components/FormControl' +import { Tr } from 'client/components/HOC' +import MultipleTags from 'client/components/MultipleTags' +import SplitPane from 'client/components/SplitPane' import { BackupsTable } from 'client/components/Tables' import BackupActions from 'client/components/Tables/Backups/actions' import BackupTabs from 'client/components/Tabs/Backup' -import SplitPane from 'client/components/SplitPane' -import MultipleTags from 'client/components/MultipleTags' -import { SubmitButton } from 'client/components/FormControl' -import { Tr } from 'client/components/HOC' -import { T, Image } from 'client/constants' +import { Image, T } from 'client/constants' +import { useLazyGetImageQuery } from 'client/features/OneApi/image' /** * Displays a list of Backups with a split pane between the list and selected row(s). @@ -38,7 +38,7 @@ import { T, Image } from 'client/constants' */ function Backups() { const [selectedRows, onSelectedRowsChange] = useState(() => []) - const actions = BackupActions() + const actions = BackupActions(selectedRows) const hasSelectedRows = selectedRows?.length > 0 const moreThanOneSelected = selectedRows?.length > 1