http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/modules/states/configuration/clusters/data-storage.pug
----------------------------------------------------------------------
diff --git 
a/modules/web-console/frontend/app/modules/states/configuration/clusters/data-storage.pug
 
b/modules/web-console/frontend/app/modules/states/configuration/clusters/data-storage.pug
index 5112591..b605fad 100644
--- 
a/modules/web-console/frontend/app/modules/states/configuration/clusters/data-storage.pug
+++ 
b/modules/web-console/frontend/app/modules/states/configuration/clusters/data-storage.pug
@@ -17,166 +17,205 @@
 include /app/helpers/jade/mixins
 
 -var form = 'dataStorageConfiguration'
--var model = 'backupItem.dataStorageConfiguration'
+-var model = '$ctrl.clonedCluster.dataStorageConfiguration'
 -var dfltRegionModel = model + '.defaultDataRegionConfiguration'
 -var dataRegionConfigurations = model + '.dataRegionConfigurations'
 
-.panel.panel-default(ng-show='$ctrl.available("2.3.0")' ng-form=form 
novalidate)
-    .panel-heading(bs-collapse-toggle ng-click=`ui.loadPanel('${form}')`)
+mixin data-region-form({modelAt, namePlaceholder, dataRegionsAt})
+    .pc-form-grid-col-60
+        +sane-ignite-form-field-text({
+            label: 'Name:',
+            model: `${modelAt}.name`,
+            name: '"name"',
+            placeholder: namePlaceholder,
+        })(
+            ng-model-options='{allowInvalid: true}'
+
+            
pc-not-in-collection='::$ctrl.Clusters.dataRegion.name.invalidValues'
+            ignite-unique=dataRegionsAt
+            ignite-unique-property='name'
+            ignite-unique-skip=`["_id", ${modelAt}]`
+        )
+            +form-field-feedback(_, 'notInCollection', 
'{{::$ctrl.Clusters.dataRegion.name.invalidValues[0]}} is reserved for internal 
use')
+            +form-field-feedback(_, 'igniteUnique', 'Name should be unique')
+
+    .pc-form-grid-col-30
+        pc-form-field-size(
+            label='Initial size:'
+            ng-model=`${modelAt}.initialSize`
+            name='initialSize'
+            placeholder='{{ $ctrl.Clusters.dataRegion.initialSize.default / 
_drISScale.value }}'
+            min='{{ ::$ctrl.Clusters.dataRegion.initialSize.min }}'
+            on-scale-change='_drISScale = $event'
+        )
+
+    .pc-form-grid-col-30
+        pc-form-field-size(
+            ng-model=`${modelAt}.maxSize`
+            ng-model-options='{allowInvalid: true}'
+            name='maxSize'
+            label='Maximum size:'
+            placeholder='{{ ::$ctrl.Clusters.dataRegion.maxSize.default }}'
+            min=`{{ $ctrl.Clusters.dataRegion.maxSize.min(${modelAt}) }}`
+        )
+
+    .pc-form-grid-col-60
+        +text('Swap file path:', `${modelAt}.swapFilePath`, '"swapFilePath"', 
'false', 'Input swap file path', 'An optional path to a memory mapped file for 
this data region')
+        
+    .pc-form-grid-col-60
+        +number('Checkpoint page buffer:', 
`${modelAt}.checkpointPageBufferSize`, '"checkpointPageBufferSize"', 'true', 
'0', '0', 'Amount of memory allocated for a checkpoint temporary buffer in 
bytes')
+
+    .pc-form-grid-col-60
+        +dropdown('Eviction mode:', `${modelAt}.pageEvictionMode`, 
'"pageEvictionMode"', 'true', 'DISABLED',
+        '[\
+            {value: "DISABLED", label: "DISABLED"},\
+            {value: "RANDOM_LRU", label: "RANDOM_LRU"},\
+            {value: "RANDOM_2_LRU", label: "RANDOM_2_LRU"}\
+        ]',
+        `An algorithm for memory pages eviction
+        <ul>
+            <li>DISABLED - Eviction is disabled</li>
+            <li>RANDOM_LRU - Once a memory region defined by a data region is 
configured, an off-heap array is allocated to track last usage timestamp for 
every individual data page</li>
+            <li>RANDOM_2_LRU - Differs from Random - LRU only in a way that 
two latest access timestamps are stored for every data page</li>
+        </ul>`)
+
+    .pc-form-grid-col-30
+        +sane-ignite-form-field-number({
+            label: 'Eviction threshold:',
+            model: `${modelAt}.evictionThreshold`,
+            name: '"evictionThreshold"',
+            placeholder: '{{ 
::$ctrl.Clusters.dataRegion.evictionThreshold.default }}',
+            min: '{{ ::$ctrl.Clusters.dataRegion.evictionThreshold.min }}',
+            max: '{{ ::$ctrl.Clusters.dataRegion.evictionThreshold.max }}',
+            step: '{{ ::$ctrl.Clusters.dataRegion.evictionThreshold.step }}',
+            tip: 'A threshold for memory pages eviction initiation'
+        })
+
+    .pc-form-grid-col-30
+        +sane-ignite-form-field-number({
+            label: 'Empty pages pool size:',
+            model: `${modelAt}.emptyPagesPoolSize`,
+            name: '"emptyPagesPoolSize"',
+            placeholder: '{{ 
::$ctrl.Clusters.dataRegion.emptyPagesPoolSize.default }}',
+            min: '{{ ::$ctrl.Clusters.dataRegion.emptyPagesPoolSize.min }}',
+            max: `{{ 
$ctrl.Clusters.dataRegion.emptyPagesPoolSize.max($ctrl.clonedCluster, 
${modelAt}) }}`,
+            tip: 'The minimal number of empty pages to be present in reuse 
lists for this data region'
+        })
+
+    .pc-form-grid-col-30
+        +sane-ignite-form-field-number({
+            label: 'Metrics sub interval count:',
+            model: `${modelAt}.subIntervals`,
+            name: '"subIntervals"',
+            placeholder: '{{ ::$ctrl.Clusters.dataRegion.subIntervals.default 
}}',
+            min: '{{ ::$ctrl.Clusters.dataRegion.subIntervals.min }}',
+            step: '{{ ::$ctrl.Clusters.dataRegion.subIntervals.step }}',
+            tip: 'A number of sub-intervals the whole rate time interval will 
be split into to calculate allocation and eviction rates'
+        })
+
+    .pc-form-grid-col-30
+        pc-form-field-size(
+            ng-model=`${modelAt}.rateTimeInterval`
+            ng-model-options='{allowInvalid: true}'
+            name='rateTimeInterval'
+            size-type='seconds'
+            label='Metrics rate time interval:'
+            placeholder='{{ $ctrl.Clusters.dataRegion.rateTimeInterval.default 
/ _rateTimeIntervalScale.value }}'
+            min=`{{ ::$ctrl.Clusters.dataRegion.rateTimeInterval.min }}`
+            tip='Time interval for allocation rate and eviction rate 
monitoring purposes'
+            on-scale-change='_rateTimeIntervalScale = $event'
+            size-scale-label='s'
+        )
+            
+    .pc-form-grid-col-60
+        +checkbox('Metrics enabled', `${modelAt}.metricsEnabled`, 
'"MemoryPolicyMetricsEnabled"',
+        'Whether memory metrics are enabled by default on node startup')
+
+    .pc-form-grid-col-60
+        +checkbox('Persistence enabled', `${modelAt}.persistenceEnabled`, 
'"RegionPersistenceEnabled" + $index',
+        'Enable Ignite Native Persistence')
+
+.pca-panel.pca-panel-default(ng-show='$ctrl.available("2.3.0")' ng-form=form 
novalidate)
+    .pca-panel-heading(bs-collapse-toggle ng-click=`ui.loadPanel('${form}')`)
         ignite-form-panel-chevron
-        label Data storage configuration
-        ignite-form-field-tooltip.tipLabel
-            | Page memory is a manageable off-heap based memory architecture 
that is split into pages of fixed size#[br]
-            | 
#[a(href="https://apacheignite.readme.io/docs/distributed-persistent-store"; 
target="_blank") More info]
-        ignite-form-revert
-    .panel-collapse(role='tabpanel' bs-collapse-target id=`${form}`)
-        .panel-body(ng-if=`$ctrl.available("2.3.0") && 
ui.isPanelLoaded('${form}')`)
-            .col-sm-6
-                .settings-row
-                    +number-min-max('Page size:', model + '.pageSize', 
'"DataStorageConfigurationPageSize"',
-                    'true', '4096', '1024', '16384', 'Every memory region is 
split on pages of fixed size')
-                .settings-row
+        .pca-panel-heading-title Data storage configuration
+        .pca-panel-heading-description
+            | Page memory is a manageable off-heap based memory architecture 
that is split into pages of fixed size. 
+            | 
#[a.link-success(href="https://apacheignite.readme.io/docs/distributed-persistent-store";
 target="_blank") More info]
+    .pca-panel-collapse(role='tabpanel' bs-collapse-target id=`${form}`)
+        .pca-panel-body(ng-if=`$ctrl.available("2.3.0") && 
ui.isPanelLoaded('${form}')`).pca-form-row
+            .pca-form-column-6.pc-form-grid-row
+                .pc-form-grid-col-30
+                    +sane-ignite-form-field-dropdown({
+                        label: 'Page size:',
+                        model: `${model}.pageSize`,
+                        name: '"DataStorageConfigurationPageSize"',
+                        options: 
`$ctrl.Clusters.dataStorageConfiguration.pageSize.values`,
+                        tip: 'Every memory region is split on pages of fixed 
size'
+                    })
+                .pc-form-grid-col-30
                     +number('Concurrency level:', model + '.concurrencyLevel', 
'"DataStorageConfigurationConcurrencyLevel"',
                     'true', 'availableProcessors', '2', 'The number of 
concurrent segments in Ignite internal page mapping tables')
-                .settings-row
-                    +ignite-form-group
-                        ignite-form-field-label
-                            | System region
-                        ignite-form-group-tooltip
-                            | System region properties
-                        .group-content
-                            .details-row
-                                +number('Initial size:', model + 
'.systemRegionInitialSize', '"DataStorageSystemRegionInitialSize"',
-                                'true', '41943040', '10485760', 'Initial size 
of a data region reserved for system cache')
-                            .details-row
-                                +number('Maximum size:', model + 
'.systemRegionMaxSize', '"DataStorageSystemRegionMaxSize"',
-                                'true', '104857600', '10485760', 'Maximum data 
region size reserved for system cache')
-                .settings-row
-                    +ignite-form-group
-                        ignite-form-field-label
-                            | Data regions
-                        ignite-form-group-tooltip
-                            | Data region configurations
-                        .group-content
-                            .details-row
-                                +ignite-form-group
-                                    ignite-form-field-label
-                                        | Default data region
-                                    ignite-form-group-tooltip
-                                        | Default data region properties
-                                    .group-content
-                                        .details-row
-                                            +text('Name:', dfltRegionModel + 
'.name', '"DfltRegionName" + $index', 'false', 'default', 'Default data region 
name')
-                                        .details-row
-                                            +number('Initial size:', 
dfltRegionModel + '.initialSize', '"DfltRegionInitialSize" + $index',
-                                            'true', '268435456', '10485760', 
'Default data region initial size')
-                                        .details-row
-                                            +number('Maximum size:', 
dfltRegionModel + '.maxSize', '"DfltRegionMaxSize" + $index',
-                                            'true', '0.2 * 
totalMemoryAvailable', '10485760', 'Default data region maximum size')
-                                        .details-row
-                                            +text('Swap file path:', 
dfltRegionModel + '.swapPath', '"DfltRegionSwapFilePath" + $index', 'false',
-                                            'Input swap file path', 'An 
optional path to a memory mapped file for default data region')
-                                        .details-row
-                                            +dropdown('Eviction mode:', 
dfltRegionModel + '.pageEvictionMode', '"DfltRegionPageEvictionMode"', 'true', 
'DISABLED',
-                                            '[\
-                                                {value: "DISABLED", label: 
"DISABLED"},\
-                                                {value: "RANDOM_LRU", label: 
"RANDOM_LRU"},\
-                                                {value: "RANDOM_2_LRU", label: 
"RANDOM_2_LRU"}\
-                                            ]',
-                                            'An algorithm for memory pages 
eviction\
-                                            <ul>\
-                                                <li>DISABLED - Eviction is 
disabled</li>\
-                                                <li>RANDOM_LRU - Once a memory 
region defined by a memory policy is configured, an off - heap array is 
allocated to track last usage timestamp for every individual data page</li>\
-                                                <li>RANDOM_2_LRU - Differs 
from Random - LRU only in a way that two latest access timestamps are stored 
for every data page</li>\
-                                            </ul>')
-                                        .details-row
-                                            +number-min-max-step('Eviction 
threshold:', dfltRegionModel + '.evictionThreshold', 
'"DfltRegionEvictionThreshold" + $index',
-                                            'true', '0.9', '0.5', '0.999', 
'0.05', 'A threshold for memory pages eviction initiation')
-                                        .details-row
-                                            +number('Empty pages pool size:', 
dfltRegionModel + '.emptyPagesPoolSize', '"DfltRegionEmptyPagesPoolSize" + 
$index',
-                                            'true', '100', '11', 'The minimal 
number of empty pages to be present in reuse lists for default data region')
-                                        .details-row
-                                            +number('Metrics sub interval 
count:', dfltRegionModel + '.metricsSubIntervalCount', 
'"DfltRegionSubIntervals" + $index',
-                                            'true', '5', '1', 'A number of 
sub-intervals the whole rate time interval will be split into to calculate 
allocation and eviction rates')
-                                        .details-row
-                                            +number('Metrics rate time 
interval:', dfltRegionModel + '.metricsRateTimeInterval', 
'"DfltRegionRateTimeInterval" + $index',
-                                            'true', '60000', '1000', 'Time 
interval for allocation rate and eviction rate monitoring purposes')
-                                        .details-row
-                                            +number('Checkpoint page buffer:', 
dfltRegionModel + '.checkpointPageBufferSize', '"DfltCheckpointPageBufferSize" 
+ $index',
-                                                'true', '0', '0', 'Amount of 
memory allocated for a checkpoint temporary buffer in bytes')
-                                        .details-row
-                                            +checkbox('Metrics enabled', 
dfltRegionModel + '.metricsEnabled', '"DfltRegionMetricsEnabled" + $index',
-                                            'Whether memory metrics are 
enabled by default on node startup')
-                                        .details-row
-                                            +checkbox('Persistence enabled', 
dfltRegionModel + '.persistenceEnabled', '"DfltRegionPersistenceEnabled" + 
$index',
-                                            'Enable Ignite Native Persistence')
-                            .details-row(ng-init='dataRegionTbl={type: 
"dataRegions", model: "dataRegionConfigurations", focusId: "name", ui: 
"data-region-table"}')
-                                +ignite-form-group()
-                                    ignite-form-field-label
-                                        | Configured data regions
-                                    ignite-form-group-tooltip
-                                        | List of configured data regions
-                                    
ignite-form-group-add(ng-click='tableNewItem(dataRegionTbl)')
-                                        | Add data region configuration
-                                    
.group-content-empty(ng-if=`!(${dataRegionConfigurations} && 
${dataRegionConfigurations}.length > 0)`)
-                                        | Not defined
-                                    
.group-content(ng-show=`${dataRegionConfigurations} && 
${dataRegionConfigurations}.length > 0` ng-repeat=`model in 
${dataRegionConfigurations} track by $index`)
-                                        hr(ng-if='$index != 0')
-                                        .settings-row
-                                            +text-enabled-autofocus('Name:', 
'model.name', '"DataRegionName" + $index', 'true', 'false', 'default', 'Data 
region name')
-                                                
+table-remove-button(dataRegionConfigurations, 'Remove memory configuration')
-                                        .settings-row
-                                            +number('Initial size:', 
'model.initialSize', '"DataRegionInitialSize" + $index',
-                                            'true', '268435456', '10485760', 
'Initial memory region size defined by this data region')
-                                        .settings-row
-                                            +number('Maximum size:', 
'model.maxSize', '"DataRegionMaxSize" + $index',
-                                            'true', '0.2 * 
totalMemoryAvailable', '10485760', 'Maximum memory region size defined by this 
data region')
-                                        .settings-row
-                                            +text('Swap file path:', 
'model.swapPath', '"DataRegionSwapPath" + $index', 'false',
-                                            'Input swap file path', 'An 
optional path to a memory mapped file for this data region')
-                                        .settings-row
-                                            +dropdown('Eviction mode:', 
'model.pageEvictionMode', '"DataRegionPageEvictionMode"', 'true', 'DISABLED',
-                                            '[\
-                                                {value: "DISABLED", label: 
"DISABLED"},\
-                                                {value: "RANDOM_LRU", label: 
"RANDOM_LRU"},\
-                                                {value: "RANDOM_2_LRU", label: 
"RANDOM_2_LRU"}\
-                                            ]',
-                                            'An algorithm for memory pages 
eviction\
-                                            <ul>\
-                                                <li>DISABLED - Eviction is 
disabled</li>\
-                                                <li>RANDOM_LRU - Once a memory 
region defined by a memory policy is configured, an off - heap array is 
allocated to track last usage timestamp for every individual data page</li>\
-                                                <li>RANDOM_2_LRU - Differs 
from Random - LRU only in a way that two latest access timestamps are stored 
for every data page</li>\
-                                            </ul>')
-                                        .settings-row
-                                            +number-min-max-step('Eviction 
threshold:', 'model.evictionThreshold', '"DataRegionEvictionThreshold" + 
$index',
-                                            'true', '0.9', '0.5', '0.999', 
'0.05', 'A threshold for memory pages eviction initiation')
-                                        .settings-row
-                                            +number('Empty pages pool size:', 
'model.emptyPagesPoolSize', '"DataRegionEmptyPagesPoolSize" + $index',
-                                            'true', '100', '11', 'The minimal 
number of empty pages to be present in reuse lists for this data region')
-                                        .settings-row
-                                            +number('Metrics sub interval 
count:', 'model.metricsSubIntervalCount', '"DataRegionSubIntervals" + $index',
-                                                'true', '5', '1', 'A number of 
sub-intervals the whole rate time interval will be split into to calculate 
allocation and eviction rates')
-                                        .settings-row
-                                            +number('Metrics rate time 
interval:', 'model.metricsRateTimeInterval', '"DataRegionRateTimeInterval" + 
$index',
-                                                'true', '60000', '1000', 'Time 
interval for allocation rate and eviction rate monitoring purposes')
-                                        .details-row
-                                            +number('Checkpoint page buffer:', 
'model.checkpointPageBufferSize', '"DataRegionCheckpointPageBufferSize" + 
$index',
-                                                'true', '0', '0', 'Amount of 
memory allocated for a checkpoint temporary buffer in bytes')
-                                        .settings-row
-                                            +checkbox('Metrics enabled', 
'model.metricsEnabled', '"DataRegionMetricsEnabled" + $index',
-                                            'Whether memory metrics are 
enabled by default on node startup')
-                                        .settings-row
-                                            +checkbox('Persistence enabled', 
'model.persistenceEnabled', '"DataRegionPersistenceEnabled" + $index',
-                                            'Enable Ignite Native Persistence')
-                .settings-row
+                .pc-form-grid-col-60.pc-form-group__text-title
+                    span System region
+                .pc-form-group.pc-form-grid-row
+                    .pc-form-grid-col-30
+                        pc-form-field-size(
+                            label='Initial size:'
+                            ng-model=`${model}.systemRegionInitialSize`
+                            name='DataStorageSystemRegionInitialSize'
+                            placeholder='{{ 
$ctrl.Clusters.dataStorageConfiguration.systemRegionInitialSize.default / 
systemRegionInitialSizeScale.value }}'
+                            min='{{ 
::$ctrl.Clusters.dataStorageConfiguration.systemRegionInitialSize.min }}'
+                            tip='Initial size of a data region reserved for 
system cache'
+                            on-scale-change='systemRegionInitialSizeScale = 
$event'
+                        )
+                    .pc-form-grid-col-30
+                        pc-form-field-size(
+                            label='Max size:'
+                            ng-model=`${model}.systemRegionMaxSize`
+                            name='DataStorageSystemRegionMaxSize'
+                            placeholder='{{ 
$ctrl.Clusters.dataStorageConfiguration.systemRegionMaxSize.default / 
systemRegionMaxSizeScale.value }}'
+                            min='{{ 
$ctrl.Clusters.dataStorageConfiguration.systemRegionMaxSize.min($ctrl.clonedCluster)
 }}'
+                            tip='Maximum data region size reserved for system 
cache'
+                            on-scale-change='systemRegionMaxSizeScale = $event'
+                        )
+                .pc-form-grid-col-60.pc-form-group__text-title
+                    span Default data region
+                .pc-form-group.pc-form-grid-row
+                    +data-region-form({
+                        modelAt: dfltRegionModel,
+                        namePlaceholder: '{{ 
::$ctrl.Clusters.dataRegion.name.default }}',
+                        dataRegionsAt: dataRegionConfigurations
+                    })
+                .pc-form-grid-col-60
+                    .ignite-form-field
+                        .ignite-form-field__label Data region configurations
+                        .ignite-form-field__control
+                            list-editable(name='dataRegionConfigurations' 
ng-model=dataRegionConfigurations)
+                                list-editable-item-edit.pc-form-grid-row
+                                    - form = '$parent.form'
+                                    +data-region-form({
+                                        modelAt: '$item',
+                                        namePlaceholder: 'Data region name',
+                                        dataRegionsAt: dataRegionConfigurations
+                                    })
+                                    - form = 'dataStorageConfiguration'
+                                list-editable-no-items
+                                    list-editable-add-item-button(
+                                        
add-item=`$ctrl.Clusters.addDataRegionConfiguration($ctrl.clonedCluster)`
+                                        label-single='data region 
configuration'
+                                        label-multiple='data region 
configurations'
+                                    )
+
+                .pc-form-grid-col-60
                     +text-enabled('Storage path:', `${model}.storagePath`, 
'"DataStoragePath"', 'true', 'false', 'db',
                     'Directory where index and partition files are stored')
-                .settings-row
+                .pc-form-grid-col-60
                     +number('Checkpoint frequency:', 
`${model}.checkpointFrequency`, '"DataStorageCheckpointFrequency"', 'true', 
'180000', '1',
                     'Frequency which is a minimal interval when the dirty 
pages will be written to the Persistent Store')
-                .settings-row
+                .pc-form-grid-col-20
                     +number('Checkpoint threads:', 
`${model}.checkpointThreads`, '"DataStorageCheckpointThreads"', 'true', '4', 
'1', 'A number of threads to use for the checkpoint purposes')
-                .settings-row
+                .pc-form-grid-col-20
                     +dropdown('Checkpoint write order:', 
`${model}.checkpointWriteOrder`, '"DataStorageCheckpointWriteOrder"', 'true', 
'SEQUENTIAL',
                     '[\
                         {value: "RANDOM", label: "RANDOM"},\
@@ -187,7 +226,7 @@ include /app/helpers/jade/mixins
                         <li>RANDOM - Pages are written in order provided by 
checkpoint pages collection iterator</li>\
                         <li>SEQUENTIAL - All checkpoint pages are collected 
into single list and sorted by page index</li>\
                     </ul>')
-                .settings-row
+                .pc-form-grid-col-20
                     +dropdown('WAL mode:', `${model}.walMode`, 
'"DataStorageWalMode"', 'true', 'DEFAULT',
                     '[\
                         {value: "DEFAULT", label: "DEFAULT"},\
@@ -202,40 +241,40 @@ include /app/helpers/jade/mixins
                         <li>BACKGROUND - does not force application&#39;s 
buffer flush</li>\
                         <li>NONE - WAL is disabled</li>\
                     </ul>')
-                .settings-row
+                .pc-form-grid-col-60
                     +text-enabled('WAL path:', `${model}.walPath`, 
'"DataStorageWalPath"', 'true', 'false', 'db/wal', 'A path to the directory 
where WAL is stored')
-                .settings-row
+                .pc-form-grid-col-60
                     +text-enabled('WAL archive path:', 
`${model}.walArchivePath`, '"DataStorageWalArchivePath"', 'true', 'false', 
'db/wal/archive', 'A path to the WAL archive directory')
-                .settings-row
+                .pc-form-grid-col-20
                     +number('WAL segments:', `${model}.walSegments`, 
'"DataStorageWalSegments"', 'true', '10', '1', 'A number of WAL segments to 
work with')
-                .settings-row
+                .pc-form-grid-col-20
                     +number('WAL segment size:', `${model}.walSegmentSize`, 
'"DataStorageWalSegmentSize"', 'true', '67108864', '0', 'Size of a WAL segment')
-                .settings-row
+                .pc-form-grid-col-20
                     +number('WAL history size:', `${model}.walHistorySize`, 
'"DataStorageWalHistorySize"', 'true', '20', '1', 'A total number of 
checkpoints to keep in the WAL history')
-                .settings-row(ng-show='$ctrl.available("2.4.0")')
+                .pc-form-grid-col-60(ng-if='$ctrl.available("2.4.0")')
                     +number('WAL buffer size:', `${model}.walBufferSize`, 
'"DataStorageWalBufferSize"', 'true', 'WAL segment size / 4', '1',
                     'Size of WAL buffer')
-                .settings-row
+                .pc-form-grid-col-30
                     +number('WAL flush frequency:', 
`${model}.walFlushFrequency`, '"DataStorageWalFlushFrequency"', 'true', '2000', 
'1',
                     'How often will be fsync, in milliseconds. In background 
mode, exist thread which do fsync by timeout')
-                .settings-row
+                .pc-form-grid-col-30
                     +number('WAL fsync delay:', `${model}.walFsyncDelayNanos`, 
'"DataStorageWalFsyncDelay"', 'true', '1000', '1', 'WAL fsync delay, in 
nanoseconds')
-                .settings-row
+                .pc-form-grid-col-60
                     +number('WAL record iterator buffer size:', 
`${model}.walRecordIteratorBufferSize`, 
'"DataStorageWalRecordIteratorBufferSize"', 'true', '67108864', '1',
                     'How many bytes iterator read from disk(for one reading), 
during go ahead WAL')
-                .settings-row
+                .pc-form-grid-col-30
                     +number('Lock wait time:', `${model}.lockWaitTime`, 
'"DataStorageLockWaitTime"', 'true', '10000', '1',
                     'Time out in milliseconds, while wait and try get file 
lock for start persist manager')
-                .settings-row
+                .pc-form-grid-col-30
                     +number('WAL thread local buffer size:', 
`${model}.walThreadLocalBufferSize`, '"DataStorageWalThreadLocalBufferSize"', 
'true', '131072', '1',
                     'Define size thread local buffer. Each thread which write 
to WAL have thread local buffer for serialize recode before write in WAL')
-                .settings-row
+                .pc-form-grid-col-30
                     +number('Metrics sub interval count:', 
`${model}.metricsSubIntervalCount`, '"DataStorageMetricsSubIntervalCount"', 
'true', '5', '1',
                     'Number of sub - intervals the whole rate time interval 
will be split into to calculate rate - based metrics')
-                .settings-row
+                .pc-form-grid-col-30
                     +number('Metrics rate time interval:', 
`${model}.metricsRateTimeInterval`, '"DataStorageMetricsRateTimeInterval"', 
'true', '60000', '1000',
                     'The length of the time interval for rate - based metrics. 
This interval defines a window over which hits will be tracked')
-                .settings-row
+                .pc-form-grid-col-30
                     +dropdown('File IO factory:', `${model}.fileIOFactory`, 
'"DataStorageFileIOFactory"', 'true', 'Default',
                     '[\
                         {value: "RANDOM", label: "RANDOM"},\
@@ -247,18 +286,19 @@ include /app/helpers/jade/mixins
                         <li>RANDOM - Pages are written in order provided by 
checkpoint pages collection iterator</li>\
                         <li>SEQUENTIAL - All checkpoint pages are collected 
into single list and sorted by page index</li>\
                     </ul>')
-                .settings-row
+                .pc-form-grid-col-30
                     +number('WAL auto archive after inactivity:', 
`${model}.walAutoArchiveAfterInactivity`, 
'"DataStorageWalAutoArchiveAfterInactivity"', 'true', '-1', '-1',
                     'Time in millis to run auto archiving segment after last 
record logging')
-                .settings-row
+                .pc-form-grid-col-60
                     +checkbox-enabled('Metrics enabled', 
`${model}.metricsEnabled`, '"DataStorageMetricsEnabled"', 'true', 'Flag 
indicating whether persistence metrics collection is enabled')
-                .settings-row
+                .pc-form-grid-col-60
                     +checkbox-enabled('Always write full pages', 
`${model}.alwaysWriteFullPages`, '"DataStorageAlwaysWriteFullPages"', 'true', 
'Flag indicating whether always write full pages')
-                .settings-row
+                .pc-form-grid-col-60
                     +checkbox('Write throttling enabled', 
`${model}.writeThrottlingEnabled`, '"DataStorageWriteThrottlingEnabled"',
                     'Throttle threads that generate dirty pages too fast 
during ongoing checkpoint')
-                .settings-row(ng-show='$ctrl.available("2.4.0")')
+                .pc-form-grid-col-60(ng-if='$ctrl.available("2.4.0")')
                     +checkbox('Enable WAL compaction', 
`${model}.walCompactionEnabled`, '"DataStorageWalCompactionEnabled"',
                     'If true, system filters and compresses WAL archive in 
background')
-            .col-sm-6
+
+            .pca-form-column-6
                 +preview-xml-java(model, 'clusterDataStorageConfiguration')

http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/modules/states/configuration/clusters/deployment.pug
----------------------------------------------------------------------
diff --git 
a/modules/web-console/frontend/app/modules/states/configuration/clusters/deployment.pug
 
b/modules/web-console/frontend/app/modules/states/configuration/clusters/deployment.pug
index 74b2acf..10244ac 100644
--- 
a/modules/web-console/frontend/app/modules/states/configuration/clusters/deployment.pug
+++ 
b/modules/web-console/frontend/app/modules/states/configuration/clusters/deployment.pug
@@ -17,28 +17,27 @@
 include /app/helpers/jade/mixins
 
 -var form = 'deployment'
--var model = 'backupItem'
--var modelDeployment = 'backupItem.deploymentSpi'
+-var model = '$ctrl.clonedCluster'
+-var modelDeployment = '$ctrl.clonedCluster.deploymentSpi'
 -var exclude = model + '.peerClassLoadingLocalClassPathExclude'
--var enabled = 'backupItem.peerClassLoadingEnabled'
+-var enabled = '$ctrl.clonedCluster.peerClassLoadingEnabled'
 -var uriListModel = modelDeployment + '.URI.uriList'
 -var scannerModel = modelDeployment + '.URI.scanners'
 -var uriDeployment = modelDeployment + '.kind === "URI"'
 -var localDeployment = modelDeployment + '.kind === "Local"'
 -var customDeployment = modelDeployment + '.kind === "Custom"'
 
-.panel.panel-default(ng-form=form novalidate)
-    .panel-heading(bs-collapse-toggle ng-click=`ui.loadPanel('${form}')`)
+.pca-panel.pca-panel-default(ng-form=form novalidate)
+    .pca-panel-heading(bs-collapse-toggle ng-click=`ui.loadPanel('${form}')`)
         ignite-form-panel-chevron
-        label Class deployment
-        ignite-form-field-tooltip.tipLabel
-            | Task and resources deployment in cluster#[br]
-            | #[a(href="https://apacheignite.readme.io/docs/deployment-modes"; 
target="_blank") More info]
-        ignite-form-revert
-    .panel-collapse(role='tabpanel' bs-collapse-target id='deployment')
-        .panel-body(ng-if=`ui.isPanelLoaded('${form}')`)
-            .col-sm-6
-                .settings-row
+        .pca-panel-heading-title Class deployment
+        .pca-panel-heading-description
+            | Task and resources deployment in cluster.
+            | 
#[a.link-success(href="https://apacheignite.readme.io/docs/deployment-modes"; 
target="_blank") More info]
+    .pca-panel-collapse(role='tabpanel' bs-collapse-target id='deployment')
+        .pca-panel-body(ng-if=`ui.isPanelLoaded('${form}')`).pca-form-row
+            .pca-form-column-6.pc-form-grid-row
+                .pc-form-grid-col-60
                     +dropdown('Deployment mode:', `${model}.deploymentMode`, 
'"deploymentMode"', 'true', 'SHARED',
                         '[\
                             {value: "PRIVATE", label: "PRIVATE"},\
@@ -54,74 +53,55 @@ include /app/helpers/jade/mixins
                             <li>SHARED - same as ISOLATED, but now tasks from 
different master nodes with the same user version and same class loader will 
share the same class loader on remote nodes</li>\
                             <li>CONTINUOUS - same as SHARED deployment mode, 
but resources will not be undeployed even after all master nodes left grid</li>\
                         </ul>')
-                .settings-row
+                .pc-form-grid-col-60
                     +checkbox('Enable peer class loading', 
`${model}.peerClassLoadingEnabled`, '"peerClassLoadingEnabled"', 
'Enables/disables peer class loading')
-                .settings-row
+                .pc-form-grid-col-60
                     +number('Missed resources cache size:', 
`${model}.peerClassLoadingMissedResourcesCacheSize`, 
'"peerClassLoadingMissedResourcesCacheSize"', enabled, '100', '0',
                         'If size greater than 0, missed resources will be 
cached and next resource request ignored<br/>\
                         If size is 0, then request for the resource will be 
sent to the remote node every time this resource is requested')
-                .settings-row
+                .pc-form-grid-col-60
                     +number('Pool size:', 
`${model}.peerClassLoadingThreadPoolSize`, '"peerClassLoadingThreadPoolSize"', 
enabled, '2', '1', 'Thread pool size to use for peer class loading')
-                .settings-row
-                    +ignite-form-group
-                        -var uniqueTip = 'Such package already exists'
-
-                        ignite-form-field-label
-                            | Local class path exclude
-                        ignite-form-group-tooltip
-                            | List of packages from the system classpath that 
need to be peer-to-peer loaded from task originating node<br/>
-                            | '*' is supported at the end of the package name 
which means that all sub-packages and their classes are included like in Java 
package import clause
-                        ignite-form-group-add(ng-show=`${enabled}` 
ng-click='(group.add = [{}])')
-                            | Add package name.
-
-                        .group-content(ng-if=`${exclude}.length`)
-                            -var model = 'obj.model';
-                            -var name = '"edit" + $index'
-                            -var valid = `${form}[${name}].$valid`
-                            -var save = `${exclude}[$index] = ${model}`
-
-                            div(ng-show=enabled)
-                                div(ng-repeat=`model in ${exclude} track by 
$index` ng-init='obj = {}')
-                                    label.col-xs-12.col-sm-12.col-md-12
-                                        .indexField
-                                            | {{ $index+1 }})
-                                        +table-remove-button(exclude, 'Remove 
package name')
-                                        span(ng-hide='field.edit')
-                                            
a.labelFormField(ng-click=`(field.edit = true) && (${model} = model)`) {{ model 
}}
-                                        span(ng-if='field.edit')
-                                            +table-java-package-field(name, 
model, exclude, valid, save, false)
-                                                +table-save-button(valid, 
save, false)
-                                                +unique-feedback(name, 
uniqueTip)
-
-                            div(ng-hide=enabled)
-                                div(ng-repeat=`model in ${exclude} track by 
$index`)
-                                    label.col-xs-12.col-sm-12.col-md-12
-                                        .labelFormField.labelField
-                                            | {{ $index+1 }})
-                                        span.labelFormField
-                                            | {{ model }}
-
-                        .group-content(ng-repeat='field in group.add')
-                            -var model = 'new';
-                            -var name = '"new"'
-                            -var valid = `${form}[${name}].$valid`
-                            -var save = `${exclude}.push(${model})`
-
-                            div(type='internal' name='Package name')
-                                label.col-xs-12.col-sm-12.col-md-12
-                                    +table-java-package-field(name, model, 
exclude, valid, save, true)
-                                        +table-save-button(valid, save, true)
-                                        +unique-feedback(name, uniqueTip)
-
-                        .group-content-empty(ng-if=`!(${exclude}.length) && 
!group.add.length`)
-                            | Not defined
+                .pc-form-grid-col-60
+                    mixin clusters-deployment-packages
+                        .ignite-form-field
+                            -let items = exclude
+                            -var uniqueTip = 'Such package already exists!'
+
+                            list-editable(
+                                ng-model=items
+                                name='localClassPathExclude'
+                                list-editable-cols=`::[{
+                                    name: "Local class path excludes:",
+                                    tip: "List of packages from the system 
classpath that need to be peer-to-peer loaded from task originating node<br/>
+                                    '*' is supported at the end of the package 
name which means that all sub-packages and their classes are included like in 
Java package import clause"
+                                }]`
+                                ng-disabled=enabledToDisabled(enabled)
+                            )
+                                list-editable-item-view {{ $item }}
+
+                                list-editable-item-edit
+                                    +list-java-package-field('Package name', 
'$item', '"packageName"', items)(
+                                        ignite-auto-focus
+                                    )
+                                        +unique-feedback('"packageName"', 
uniqueTip)
+
+                                list-editable-no-items
+                                    list-editable-add-item-button(
+                                        
add-item=`$editLast($ctrl.Clusters.addPeerClassLoadingLocalClassPathExclude(${model}))`
+                                        label-single='package'
+                                        label-multiple='packages'
+                                    )
+
+                    -var form = '$parent.form'
+                    +clusters-deployment-packages
+                    -var form = 'deployment'
 
                 //- Since ignite 2.0
-                .settings-row(ng-if='$ctrl.available("2.0.0")')
+                .pc-form-grid-col-60(ng-if='$ctrl.available("2.0.0")')
                     +java-class('Class loader:', model + '.classLoader', 
'"classLoader"', 'true', 'false',
                         'Loader which will be used for instantiating execution 
context')
 
-                .settings-row
+                .pc-form-grid-col-60
                     +dropdown('Deployment variant:', modelDeployment + 
'.kind', '"deploymentKind"', 'true', 'Default',
                         '[\
                             {value: "URI", label: "URI"},\
@@ -136,108 +116,80 @@ include /app/helpers/jade/mixins
                             <li>Custom - Custom implementation of 
DeploymentSpi</li>\
                             <li>Default - Default configuration of 
LocalDeploymentSpi will be used</li>\
                         </ul>')
-                .panel-details(ng-show=uriDeployment)
-                    .details-row
-                        +ignite-form-group()
-                            -var uniqueTip = 'Such URI already configured'
-
-                            ignite-form-field-label
-                                | URI list
-                            ignite-form-group-tooltip
-                                | List of URI which point to GAR file and 
which should be scanned by SPI for the new tasks
-                            ignite-form-group-add(ng-click='(group.add = 
[{}])')
-                                | Add URI.
-
-                            .group-content(ng-if=uriListModel + '.length')
-                                -var model = 'obj.model';
-                                -var name = '"edit" + $index'
-                                -var valid = `${form}[${name}].$valid`
-                                -var save = `${uriListModel}[$index] = 
${model}`
-
-                                div(ng-repeat=`model in ${uriListModel} track 
by $index` ng-init='obj = {}')
-                                    label.col-xs-12.col-sm-12.col-md-12
-                                        .indexField
-                                            | {{ $index+1 }})
-                                        +table-remove-button(uriListModel, 
'Remove URI')
-                                        span(ng-hide='field.edit')
-                                            
a.labelFormField(ng-click=`(field.edit = true) && (${model} = model)`) {{ model 
}}
-                                        span(ng-if='field.edit')
-                                            +table-url-field(name, model, 
uriListModel, valid, save, false)
-                                                +table-save-button(valid, 
save, false)
-                                                +unique-feedback(name, 
uniqueTip)
-
-                            .group-content(ng-repeat='field in group.add')
-                                -var model = 'new';
-                                -var name = '"new"'
-                                -var valid = `${form}[${name}].$valid`
-                                -var save = `${uriListModel}.push(${model})`
-
-                                div(type='internal' name='URI')
-                                    label.col-xs-12.col-sm-12.col-md-12
-                                        +table-url-field(name, model, 
uriListModel, valid, save, true)
-                                            +table-save-button(valid, save, 
true)
-                                            +unique-feedback(name, uniqueTip)
-
-                            
.group-content-empty(ng-if=`!(${uriListModel}.length) && !group.add.length`)
-                                | Not defined
-                    .details-row
+                .pc-form-group(ng-show=uriDeployment).pc-form-grid-row
+                    .pc-form-grid-col-60
+                        mixin clusters-deployment-uri
+                            .ignite-form-field
+                                -let items = uriListModel
+                                -var uniqueTip = 'Such URI already configured!'
+
+                                list-editable(
+                                    ng-model=items
+                                    name='uriList'
+                                    list-editable-cols=`::[{
+                                        name: "URI list:",
+                                        tip: "List of URI which point to GAR 
file and which should be scanned by SPI for the new tasks"
+                                    }]`
+                                )
+                                    list-editable-item-view {{ $item }}
+
+                                    list-editable-item-edit
+                                        +list-url-field('URL', '$item', 
'"url"', items)
+                                            +unique-feedback('"url"', 
uniqueTip)
+
+                                    list-editable-no-items
+                                        list-editable-add-item-button(
+                                            add-item=`$editLast((${items} = 
${items} || []).push(''))`
+                                            label-single='URI'
+                                            label-multiple='URIs'
+                                        )
+
+                        - var form = '$parent.form'
+                        +clusters-deployment-uri
+                        - var form = 'deployment'
+
+                    .pc-form-grid-col-60
                         +text('Temporary directory path:', modelDeployment + 
'.URI.temporaryDirectoryPath', '"DeploymentURITemporaryDirectoryPath"', 
'false', 'Temporary directory path',
                         'Absolute path to temporary directory which will be 
used by deployment SPI to keep all deployed classes in')
-                    .details-row
-                        +ignite-form-group()
-                            -var uniqueTip = 'Such scanner already configured'
-
-                            ignite-form-field-label
-                                | Scanner list
-                            ignite-form-group-tooltip
-                                | List of URI deployment scanners
-                            ignite-form-group-add(ng-click='(group.add = 
[{}])')
-                                | Add scanner
-
-                            .group-content(ng-if=scannerModel + '.length')
-                                -var model = 'obj.model';
-                                -var name = '"edit" + $index'
-                                -var valid = `${form}[${name}].$valid`
-                                -var save = `${scannerModel}[$index] = 
${model}`
-
-                                div(ng-repeat=`model in ${scannerModel} track 
by $index` ng-init='obj = {}')
-                                    label.col-xs-12.col-sm-12.col-md-12
-                                        .indexField
-                                            | {{ $index+1 }})
-                                        +table-remove-button(scannerModel, 
'Remove scanner')
-                                        span(ng-hide='field.edit')
-                                            
a.labelFormField(ng-click=`(field.edit = true) && (${model} = model)`) {{ model 
}}
-                                        span(ng-if='field.edit')
-                                            
+table-java-class-field('Scanner:', name, model, scannerModel, valid, save, 
false)
-                                                +table-save-button(valid, 
save, false)
-                                                +unique-feedback(name, 
uniqueTip)
-
-                            .group-content(ng-repeat='field in group.add')
-                                -var model = 'new';
-                                -var name = '"new"'
-                                -var valid = `${form}[${name}].$valid`
-                                -var save = `${scannerModel}.push(${model})`
-
-                                div(type='internal' name='Scanner')
-                                    label.col-xs-12.col-sm-12.col-md-12
-                                        //- (lbl, name, model, items, valid, 
save, newItem)
-                                        +table-java-class-field('Scanner:', 
name, model, scannerModel, valid, save, true)
-                                            +table-save-button(valid, save, 
true)
-                                            +unique-feedback(name, uniqueTip)
-
-                            
.group-content-empty(ng-if=`!(${scannerModel}.length) && !group.add.length`)
-                                | Not defined
-                    .details-row
+                    .pc-form-grid-col-60
+                        mixin clusters-deployment-scanner
+                            .ignite-form-field
+                                -let items = scannerModel
+                                -var uniqueTip = 'Such scanner already 
configured!'
+
+                                list-editable(
+                                    ng-model=items
+                                    name='scannerModel'
+                                    list-editable-cols=`::[{name: "URI 
deployment scanners:"}]`
+                                )
+                                    list-editable-item-view {{ $item }}
+
+                                    list-editable-item-edit
+                                        +list-java-class-field('Scanner', 
'$item', '"scanner"', items)
+                                            +unique-feedback('"scanner"', 
uniqueTip)
+
+                                    list-editable-no-items
+                                        list-editable-add-item-button(
+                                            add-item=`$editLast((${items} = 
${items} || []).push(''))`
+                                            label-single='scanner'
+                                            label-multiple='scanners'
+                                        )
+
+                        - var form = '$parent.form'
+                        +clusters-deployment-scanner
+                        - var form = 'deployment'
+
+                    .pc-form-grid-col-60
                         +java-class('Listener:', 
`${modelDeployment}.URI.listener`, '"DeploymentURIListener"', 'true', 'false', 
'Deployment event listener', uriDeployment)
-                    .details-row
+                    .pc-form-grid-col-60
                         +checkbox('Check MD5', 
`${modelDeployment}.URI.checkMd5`, '"DeploymentURICheckMd5"', 'Exclude files 
with same md5s from deployment')
-                    .details-row
+                    .pc-form-grid-col-60
                         +checkbox('Encode URI', 
`${modelDeployment}.URI.encodeUri`, '"DeploymentURIEncodeUri"', 'URI must be 
encoded before usage')
-                .panel-details(ng-show=localDeployment)
-                    .details-row
+                .pc-form-group(ng-show=localDeployment).pc-form-grid-row
+                    .pc-form-grid-col-60
                         +java-class('Listener:', 
`${modelDeployment}.Local.listener`, '"DeploymentLocalListener"', 'true', 
'false', 'Deployment event listener', localDeployment)
-                .panel-details(ng-show=customDeployment)
-                    .details-row
+                .pc-form-group(ng-show=customDeployment).pc-form-grid-row
+                    .pc-form-grid-col-60
                         +java-class('Class:', 
`${modelDeployment}.Custom.className`, '"DeploymentCustom"', 'true', 
customDeployment, 'DeploymentSpi implementation class', customDeployment)
-            .col-sm-6
+            .pca-form-column-6
                 +preview-xml-java(model, 'clusterDeployment')

http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/modules/states/configuration/clusters/discovery.pug
----------------------------------------------------------------------
diff --git 
a/modules/web-console/frontend/app/modules/states/configuration/clusters/discovery.pug
 
b/modules/web-console/frontend/app/modules/states/configuration/clusters/discovery.pug
index 5718755..64bdc74 100644
--- 
a/modules/web-console/frontend/app/modules/states/configuration/clusters/discovery.pug
+++ 
b/modules/web-console/frontend/app/modules/states/configuration/clusters/discovery.pug
@@ -17,76 +17,84 @@
 include /app/helpers/jade/mixins
 
 -var form = 'discovery'
--var model = 'backupItem.discovery'
+-var model = '$ctrl.clonedCluster.discovery'
 
-.panel.panel-default(ng-form=form novalidate)
-    .panel-heading(bs-collapse-toggle ng-click=`ui.loadPanel('${form}')`)
+.pca-panel.pca-panel-default(ng-form=form novalidate)
+    .pca-panel-heading(bs-collapse-toggle ng-click=`ui.loadPanel('${form}')`)
         ignite-form-panel-chevron
-        label Discovery
-        ignite-form-field-tooltip.tipLabel
-            | TCP/IP discovery configuration#[br]
-            | #[a(href="https://apacheignite.readme.io/docs/cluster-config"; 
target="_blank") More info]
-        ignite-form-revert
-    .panel-collapse(role='tabpanel' bs-collapse-target id=`${form}`)
-        .panel-body(ng-if=`ui.isPanelLoaded('${form}')`)
-            .col-sm-6
-                .settings-row
+        .pca-panel-heading-title Discovery
+        .pca-panel-heading-description
+            | TCP/IP discovery configuration. 
+            | 
#[a.link-success(href="https://apacheignite.readme.io/docs/cluster-config"; 
target="_blank") More info]
+    .pca-panel-collapse(role='tabpanel' bs-collapse-target id=`${form}`)
+        .pca-panel-body(ng-if=`ui.isPanelLoaded('${form}')`).pca-form-row
+            .pca-form-column-6.pc-form-grid-row
+                .pc-form-grid-col-20
                     +text-ip-address('Local address:', 
`${model}.localAddress`, '"discoLocalAddress"', 'true', '228.1.2.4',
                         'Local host IP address that discovery SPI uses<br/>\
                         If not provided a first found non-loopback address 
will be used')
-                .settings-row
+                .pc-form-grid-col-20
                     +number-min-max('Local port:', `${model}.localPort`, 
'"discoLocalPort"', 'true', '47500', '1024', '65535', 'Local port which node 
uses')
-                .settings-row
+                .pc-form-grid-col-20
                     +number('Local port range:', `${model}.localPortRange`, 
'"discoLocalPortRange"', 'true', '100', '1', 'Local port range')
-                .settings-row
+                .pc-form-grid-col-60
                     +java-class('Address resolver:', 
`${model}.addressResolver`, '"discoAddressResolver"', 'true', 'false',
                         'Provides resolution between external and internal 
addresses')
-                .settings-row
+                .pc-form-grid-col-30
                     +number('Socket timeout:', `${model}.socketTimeout`, 
'"socketTimeout"', 'true', '5000', '0', 'Socket operations timeout')
-                .settings-row
-                    +number('Acknowledgement timeout:', `${model}.ackTimeout`, 
'"ackTimeout"', 'true', '5000', '0', 'Message acknowledgement timeout')
-                .settings-row
+                .pc-form-grid-col-30
+                    +sane-ignite-form-field-number({
+                        label: 'Acknowledgement timeout:',
+                        model: `${model}.ackTimeout`,
+                        name: '"ackTimeout"',
+                        disabled: 'false',
+                        placeholder: '5000',
+                        min: '0',
+                        max: `{{ ${model}.maxAckTimeout || 600000 }}`,
+                        tip: 'Message acknowledgement timeout'
+                    })
+                        +form-field-feedback('"ackTimeout"', 'max', 
`Acknowledgement timeout should be less than max acknowledgement timeout ({{ 
${model}.maxAckTimeout || 60000 }}).`)
+                .pc-form-grid-col-30
                     +number('Max acknowledgement timeout:', 
`${model}.maxAckTimeout`, '"maxAckTimeout"', 'true', '600000', '0', 'Maximum 
message acknowledgement timeout')
-                .settings-row
+                .pc-form-grid-col-30
                     +number('Network timeout:', `${model}.networkTimeout`, 
'"discoNetworkTimeout"', 'true', '5000', '1', 'Timeout to use for network 
operations')
-                .settings-row
+                .pc-form-grid-col-30
                     +number('Join timeout:', `${model}.joinTimeout`, 
'"joinTimeout"', 'true', '0', '0',
                         'Join timeout<br/>' +
                         '0 means wait forever')
-                .settings-row
+                .pc-form-grid-col-30
                     +number('Thread priority:', `${model}.threadPriority`, 
'"threadPriority"', 'true', '10', '1', 'Thread priority for all threads started 
by SPI')
 
                 //- Removed in ignite 2.0
-                div(ng-if='$ctrl.available(["1.0.0", "2.0.0"])')
-                    .settings-row
-                        +number('Heartbeat frequency:', 
`${model}.heartbeatFrequency`, '"heartbeatFrequency"', 'true', '2000', '1', 
'Heartbeat messages issuing frequency')
-                    .settings-row
-                        +number('Max heartbeats miss w/o init:', 
`${model}.maxMissedHeartbeats`, '"maxMissedHeartbeats"', 'true', '1', '1',
-                            'Max heartbeats count node can miss without 
initiating status check')
-                    .settings-row
-                        +number('Max missed client heartbeats:', 
`${model}.maxMissedClientHeartbeats`, '"maxMissedClientHeartbeats"', 'true', 
'5', '1',
-                            'Max heartbeats count node can miss without 
failing client node')
+                .pc-form-grid-col-60(ng-if='$ctrl.available(["1.0.0", 
"2.0.0"])')
+                    +number('Heartbeat frequency:', 
`${model}.heartbeatFrequency`, '"heartbeatFrequency"', 'true', '2000', '1', 
'Heartbeat messages issuing frequency')
+                .pc-form-grid-col-30
+                    +number('Max heartbeats miss w/o init:', 
`${model}.maxMissedHeartbeats`, '"maxMissedHeartbeats"', 'true', '1', '1',
+                        'Max heartbeats count node can miss without initiating 
status check')
+                .pc-form-grid-col-30(ng-if-end)
+                    +number('Max missed client heartbeats:', 
`${model}.maxMissedClientHeartbeats`, '"maxMissedClientHeartbeats"', 'true', 
'5', '1',
+                        'Max heartbeats count node can miss without failing 
client node')
 
-                .settings-row
+                .pc-form-grid-col-60
                     +number('Topology history:', `${model}.topHistorySize`, 
'"topHistorySize"', 'true', '1000', '0', 'Size of topology snapshots history')
-                .settings-row
+                .pc-form-grid-col-60
                     +java-class('Discovery listener:', `${model}.listener`, 
'"discoListener"', 'true', 'false', 'Listener for grid node discovery events')
-                .settings-row
+                .pc-form-grid-col-60
                     +java-class('Data exchange:', `${model}.dataExchange`, 
'"dataExchange"', 'true', 'false', 'Class name of handler for initial data 
exchange between Ignite nodes')
-                .settings-row
+                .pc-form-grid-col-60
                     +java-class('Metrics provider:', 
`${model}.metricsProvider`, '"metricsProvider"', 'true', 'false', 'Class name 
of metric provider to discovery SPI')
-                .settings-row
+                .pc-form-grid-col-30
                     +number('Reconnect count:', `${model}.reconnectCount`, 
'"discoReconnectCount"', 'true', '10', '1', 'Reconnect attempts count')
-                .settings-row
+                .pc-form-grid-col-30
                     +number('Statistics frequency:', 
`${model}.statisticsPrintFrequency`, '"statisticsPrintFrequency"', 'true', '0', 
'1', 'Statistics print frequency')
-                .settings-row
+                .pc-form-grid-col-60
                     +number('IP finder clean frequency:', 
`${model}.ipFinderCleanFrequency`, '"ipFinderCleanFrequency"', 'true', '60000', 
'1', 'IP finder clean frequency')
-                .settings-row
+                .pc-form-grid-col-60
                     +java-class('Node authenticator:', 
`${model}.authenticator`, '"authenticator"', 'true', 'false', 'Class name of 
node authenticator implementation')
-                .settings-row
+                .pc-form-grid-col-60
                     +checkbox('Force server mode', `${model}.forceServerMode`, 
'"forceServerMode"', 'Force start TCP/IP discovery in server mode')
-                .settings-row
+                .pc-form-grid-col-60
                     +checkbox('Client reconnect disabled', 
`${model}.clientReconnectDisabled`, '"clientReconnectDisabled"',
                         'Disable try of client to reconnect after server 
detected client node failure')
-            .col-sm-6
+            .pca-form-column-6
                 +preview-xml-java(model, 'clusterDiscovery')

http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/modules/states/configuration/clusters/events.pug
----------------------------------------------------------------------
diff --git 
a/modules/web-console/frontend/app/modules/states/configuration/clusters/events.pug
 
b/modules/web-console/frontend/app/modules/states/configuration/clusters/events.pug
index 2f5cc31..45e0936 100644
--- 
a/modules/web-console/frontend/app/modules/states/configuration/clusters/events.pug
+++ 
b/modules/web-console/frontend/app/modules/states/configuration/clusters/events.pug
@@ -17,54 +17,53 @@
 include /app/helpers/jade/mixins
 
 -var form = 'events'
--var model = 'backupItem'
+-var model = '$ctrl.clonedCluster'
 -var modelEventStorage = model + '.eventStorage'
 -var modelEventStorageKind = modelEventStorage + '.kind'
 -var eventStorageMemory = modelEventStorageKind + ' === "Memory"'
 -var eventStorageCustom = modelEventStorageKind + ' === "Custom"'
 
-.panel.panel-default(ng-form=form novalidate)
-    .panel-heading(bs-collapse-toggle ng-click=`ui.loadPanel('${form}')`)
+.pca-panel.pca-panel-default(ng-form=form novalidate)
+    .pca-panel-heading(bs-collapse-toggle ng-click=`ui.loadPanel('${form}')`)
         ignite-form-panel-chevron
-        label Events
-        ignite-form-field-tooltip.tipLabel
-            | Grid events are used for notification about what happens within 
the grid#[br]
-            | #[a(href="https://apacheignite.readme.io/docs/events"; 
target="_blank") More info]
-        ignite-form-revert
-    .panel-collapse(role='tabpanel' bs-collapse-target id=`${form}`)
-        .panel-body(ng-if=`ui.isPanelLoaded('${form}')`)
-            .col-sm-6
-                .settings-row(ng-if='$ctrl.available(["1.0.0", "2.0.0"])')
-                    +dropdown('Event storage:', modelEventStorageKind, 
'"eventStorageKind"', 'true', 'Disabled', 'eventStorage',
+        .pca-panel-heading-title Events
+        .pca-panel-heading-description
+            | Grid events are used for notification about what happens within 
the grid. 
+            | 
#[a.link-success(href="https://apacheignite.readme.io/docs/events"; 
target="_blank") More info]
+    .pca-panel-collapse(role='tabpanel' bs-collapse-target id=`${form}`)
+        .pca-panel-body(ng-if=`ui.isPanelLoaded('${form}')`).pca-form-row
+            .pca-form-column-6.pc-form-grid-row
+                .pc-form-grid-col-60(ng-if='$ctrl.available(["1.0.0", 
"2.0.0"])')
+                    +dropdown('Event storage:', modelEventStorageKind, 
'"eventStorageKind"', 'true', 'Disabled', '$ctrl.eventStorage',
                     'Regulate how grid store events locally on node\
                     <ul>\
                         <li>Memory - All events are kept in the FIFO queue 
in-memory</li>\
                         <li>Custom - Custom implementation of event storage 
SPI</li>\
                     </ul>')
-                .settings-row(ng-if='$ctrl.available("2.0.0")')
-                    +dropdown('Event storage:', modelEventStorageKind, 
'"eventStorageKind"', 'true', 'Disabled', 'eventStorage',
+                .pc-form-grid-col-60(ng-if='$ctrl.available("2.0.0")')
+                    +dropdown('Event storage:', modelEventStorageKind, 
'"eventStorageKind"', 'true', 'Disabled', '$ctrl.eventStorage',
                     'Regulate how grid store events locally on node\
                     <ul>\
                         <li>Memory - All events are kept in the FIFO queue 
in-memory</li>\
                         <li>Custom - Custom implementation of event storage 
SPI</li>\
                         <li>Disabled - Events are not collected</li>\
                     </ul>')
-                div(ng-show=eventStorageMemory)
-                    .settings-row
+                .pc-form-group.pc-form-grid-row(ng-if=modelEventStorageKind)
+                    .pc-form-grid-col-30(ng-if-start=eventStorageMemory)
                         +number('Events expiration time:', 
`${modelEventStorage}.Memory.expireAgeMs`, '"EventStorageExpireAgeMs"', 'true', 
'Long.MAX_VALUE', '1', 'All events that exceed this value will be removed from 
the queue when next event comes')
-                    .settings-row
+                    .pc-form-grid-col-30
                         +number('Events queue size:', 
`${modelEventStorage}.Memory.expireCount`, '"EventStorageExpireCount"', 'true', 
'10000', '1', 'Events will be filtered out when new request comes')
-                    .settings-row
+                    .pc-form-grid-col-60(ng-if-end)
                         +java-class('Filter:', 
`${modelEventStorage}.Memory.filter`, '"EventStorageFilter"', 'true', 'false',
                         'Filter for events to be recorded<br/>\
                         Should be implementation of 
o.a.i.lang.IgnitePredicate&lt;o.a.i.events.Event&gt;', eventStorageMemory)
 
-                .settings-row(ng-show=eventStorageCustom)
-                    +java-class('Class:', 
`${modelEventStorage}.Custom.className`, '"EventStorageCustom"', 'true', 
eventStorageCustom, 'Event storage implementation class name', 
eventStorageCustom)
+                    .pc-form-grid-col-60(ng-if=eventStorageCustom)
+                        +java-class('Class:', 
`${modelEventStorage}.Custom.className`, '"EventStorageCustom"', 'true', 
eventStorageCustom, 'Event storage implementation class name', 
eventStorageCustom)
 
-                .settings-row(ng-show=modelEventStorageKind)
-                    +dropdown-multiple('Include type:', 
`${model}.includeEventTypes`, '"includeEventTypes"', true, 'Choose recorded 
event types', '', 'eventGroups',
-                    'Array of event types, which will be recorded by 
GridEventStorageManager#record(Event)<br/>\
-                    Note, that either the include event types or the exclude 
event types can be established')
-            .col-sm-6
+                    .pc-form-grid-col-60
+                        +dropdown-multiple('Include type:', 
`${model}.includeEventTypes`, '"includeEventTypes"', true, 'Choose recorded 
event types', '', '$ctrl.eventGroups',
+                        'Array of event types, which will be recorded by 
GridEventStorageManager#record(Event)<br/>\
+                        Note, that either the include event types or the 
exclude event types can be established')
+            .pca-form-column-6
                 +preview-xml-java(model, 'clusterEvents')

http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/modules/states/configuration/clusters/failover.pug
----------------------------------------------------------------------
diff --git 
a/modules/web-console/frontend/app/modules/states/configuration/clusters/failover.pug
 
b/modules/web-console/frontend/app/modules/states/configuration/clusters/failover.pug
index d61516c..2f6dbed 100644
--- 
a/modules/web-console/frontend/app/modules/states/configuration/clusters/failover.pug
+++ 
b/modules/web-console/frontend/app/modules/states/configuration/clusters/failover.pug
@@ -16,67 +16,77 @@
 
 include /app/helpers/jade/mixins
 
--var model = 'backupItem'
+-var model = '$ctrl.clonedCluster'
 -var form = 'failoverSpi'
 -var failoverSpi = model + '.failoverSpi'
--var failoverCustom = 'model.kind === "Custom"'
+-var failoverCustom = '$item.kind === "Custom"'
 
-.panel.panel-default(ng-form=form novalidate)
-    .panel-heading(bs-collapse-toggle ng-click=`ui.loadPanel('${form}')`)
+.pca-panel.pca-panel-default(ng-form=form novalidate)
+    .pca-panel-heading(bs-collapse-toggle ng-click=`ui.loadPanel('${form}')`)
         ignite-form-panel-chevron
-        label Failover configuration
-        ignite-form-field-tooltip.tipLabel
-            | Failover SPI provides ability to supply custom logic for 
handling failed execution of a grid job#[br]
-            | #[a(href="https://apacheignite.readme.io/docs/fault-tolerance"; 
target="_blank") More info]
-        ignite-form-revert
-    .panel-collapse(role='tabpanel' bs-collapse-target id=`${form}`)
-        .panel-body(ng-if=`ui.isPanelLoaded('${form}')`)
-            .col-sm-6
+        .pca-panel-heading-title Failover configuration
+        .pca-panel-heading-description
+            | Failover SPI provides ability to supply custom logic for 
handling failed execution of a grid job. 
+            | 
#[a.link-success(href="https://apacheignite.readme.io/docs/fault-tolerance"; 
target="_blank") More info]
+    .pca-panel-collapse(role='tabpanel' bs-collapse-target id=`${form}`)
+        .pca-panel-body(ng-if=`ui.isPanelLoaded('${form}')`).pca-form-row
+            .pca-form-column-6.pc-form-grid-row
                 //- Since ignite 2.0
-                div(ng-if='$ctrl.available("2.0.0")')
-                    .settings-row
-                        +number('Failure detection timeout:', model + 
'.failureDetectionTimeout', '"failureDetectionTimeout"', 'true',
-                            '10000', '1', 'Failure detection timeout is used 
to determine how long the communication or discovery SPIs should wait before 
considering a remote connection failed')
-                    .settings-row
-                        +number('Client failure detection timeout:', model + 
'.clientFailureDetectionTimeout', '"clientFailureDetectionTimeout"', 'true',
-                            '30000', '1', 'Failure detection timeout is used 
to determine how long the communication or discovery SPIs should wait before 
considering a remote connection failed')
+                .pc-form-grid-col-60(ng-if-start='$ctrl.available("2.0.0")')
+                    +number('Failure detection timeout:', model + 
'.failureDetectionTimeout', '"failureDetectionTimeout"', 'true',
+                        '10000', '1', 'Failure detection timeout is used to 
determine how long the communication or discovery SPIs should wait before 
considering a remote connection failed')
+                .pc-form-grid-col-60(ng-if-end)
+                    +number('Client failure detection timeout:', model + 
'.clientFailureDetectionTimeout', '"clientFailureDetectionTimeout"', 'true',
+                        '30000', '1', 'Failure detection timeout is used to 
determine how long the communication or discovery SPIs should wait before 
considering a remote connection failed')
 
-                .settings-row(ng-init='failoverSpiTbl={type: "failoverSpi", 
model: "failoverSpi", focusId: "kind", ui: "failover-table"}')
-                    +ignite-form-group()
-                        ignite-form-field-label
-                            | Failover SPI configurations
-                        ignite-form-group-tooltip
-                            | Failover SPI configurations
-                        
ignite-form-group-add(ng-click='tableNewItem(failoverSpiTbl)')
-                            | Add failover SPI
-                        .group-content-empty(ng-if=`!(${failoverSpi} && 
${failoverSpi}.length > 0)`)
-                            | Not defined
-                        .group-content(ng-show=`${failoverSpi} && 
${failoverSpi}.length > 0` ng-repeat=`model in ${failoverSpi} track by $index`)
-                            hr(ng-if='$index != 0')
-                            .settings-row
-                                +dropdown-required-autofocus('Failover SPI:', 
'model.kind', '"failoverKind" + $index', 'true', 'true', 'Choose Failover SPI', 
'[\
-                                        {value: "JobStealing", label: "Job 
stealing"},\
-                                        {value: "Never", label: "Never"},\
-                                        {value: "Always", label: "Always"},\
-                                        {value: "Custom", label: "Custom"}\
-                                    ]', 'Provides ability to supply custom 
logic for handling failed execution of a grid job\
-                                    <ul>\
-                                        <li>Job stealing - Supports job 
stealing from over-utilized nodes to under-utilized nodes</li>\
-                                        <li>Never - Jobs are ordered as they 
arrived</li>\
-                                        <li>Always - Jobs are first ordered by 
their priority</li>\
-                                        <li>Custom - Jobs are activated 
immediately on arrival to mapped node</li>\
-                                        <li>Default - Default FailoverSpi 
implementation</li>\
-                                    </ul>')
+                .pc-form-grid-col-60(ng-init='failoverSpiTbl={type: 
"failoverSpi", model: "failoverSpi", focusId: "kind", ui: "failover-table"}')
+                    mixin clusters-failover-spi
+                        .ignite-form-field
+                            +ignite-form-field__label('Failover SPI 
configurations:', '"failoverSpi"')
+                                +tooltip(`Failover SPI configurations`)
+                            .ignite-form-field__control
+                                -let items = failoverSpi
 
-                                    +table-remove-button(failoverSpi, 'Remove 
Failover SPI')
-                            .settings-row(ng-show='model.kind === 
"JobStealing"')
-                                +number('Maximum failover attempts:', 
'model.JobStealing.maximumFailoverAttempts', '"jsMaximumFailoverAttempts" + 
$index', 'true', '5', '0',
-                                    'Maximum number of attempts to execute a 
failed job on another node')
-                            .settings-row(ng-show='model.kind === "Always"')
-                                +number('Maximum failover attempts:', 
'model.Always.maximumFailoverAttempts', '"alwaysMaximumFailoverAttempts" + 
$index', 'true', '5', '0',
-                                    'Maximum number of attempts to execute a 
failed job on another node')
-                            .settings-row(ng-show=failoverCustom)
-                                +java-class('SPI implementation', 
'model.Custom.class', '"failoverSpiClass" + $index', 'true', failoverCustom,
-                                    'Custom FailoverSpi implementation class 
name.', failoverCustom)
-            .col-sm-6
+                                list-editable(ng-model=items 
name='failoverSpi')
+                                    list-editable-item-edit
+                                        - form = '$parent.form'
+                                        .settings-row
+                                            +sane-ignite-form-field-dropdown({
+                                                required: true,
+                                                label: 'Failover SPI:',
+                                                model: '$item.kind',
+                                                name: '"failoverKind"',
+                                                placeholder: 'Choose Failover 
SPI',
+                                                options: 
'::$ctrl.Clusters.failoverSpis',
+                                                tip: `
+                                                Provides ability to supply 
custom logic for handling failed execution of a grid job
+                                                <ul>
+                                                    <li>Job stealing - 
Supports job stealing from over-utilized nodes to under-utilized nodes</li>
+                                                    <li>Never - Jobs are 
ordered as they arrived</li>
+                                                    <li>Always - Jobs are 
first ordered by their priority</li>
+                                                    <li>Custom - Jobs are 
activated immediately on arrival to mapped node</li>
+                                                    <li>Default - Default 
FailoverSpi implementation</li>
+                                                </ul>`
+                                            })
+
+                                        .settings-row(ng-show='$item.kind === 
"JobStealing"')
+                                            +number('Maximum failover 
attempts:', '$item.JobStealing.maximumFailoverAttempts', 
'"jsMaximumFailoverAttempts"', 'true', '5', '0',
+                                                'Maximum number of attempts to 
execute a failed job on another node')
+                                        .settings-row(ng-show='$item.kind === 
"Always"')
+                                            +number('Maximum failover 
attempts:', '$item.Always.maximumFailoverAttempts', 
'"alwaysMaximumFailoverAttempts"', 'true', '5', '0',
+                                                'Maximum number of attempts to 
execute a failed job on another node')
+                                        .settings-row(ng-show=failoverCustom)
+                                            +java-class('SPI implementation', 
'$item.Custom.class', '"failoverSpiClass"', 'true', failoverCustom,
+                                                'Custom FailoverSpi 
implementation class name.', failoverCustom)
+
+                                    list-editable-no-items
+                                        list-editable-add-item-button(
+                                            add-item=`(${items} = ${items} || 
[]).push({})`
+                                            label-single='failover SPI'
+                                            label-multiple='failover SPIs'
+                                        )
+
+                    +clusters-failover-spi
+
+            .pca-form-column-6
                 +preview-xml-java(model, 'clusterFailover')

Reply via email to