You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ak...@apache.org on 2018/04/02 12:24:31 UTC

[08/24] ignite git commit: IGNITE-5466 Web Console: Configuration reworked to cluster centric model: 1. Reworked data model. 2. Implemented migrations. 3. Reworked UI for all screens. 4. Reworked validation. 5. Many refactorings to improve code base

http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/modules/states/configuration/clusters/service.pug
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/configuration/clusters/service.pug b/modules/web-console/frontend/app/modules/states/configuration/clusters/service.pug
index cf4c27a..7f9d75f 100644
--- a/modules/web-console/frontend/app/modules/states/configuration/clusters/service.pug
+++ b/modules/web-console/frontend/app/modules/states/configuration/clusters/service.pug
@@ -17,72 +17,76 @@
 include /app/helpers/jade/mixins
 
 -var form = 'serviceConfiguration'
--var model = 'backupItem.serviceConfigurations'
+-var model = '$ctrl.clonedCluster.serviceConfigurations'
 
-.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 Service configuration
-        ignite-form-field-tooltip.tipLabel
-            | Service Grid allows for deployments of arbitrary user-defined services on the cluster#[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
-                .settings-row(ng-init='serviceConfigurationsTbl={type: "serviceConfigurations", model: "serviceConfigurations", focusId: "kind", ui: "failover-table"}')
-                    +ignite-form-group()
-                        ignite-form-field-label
-                            | Service configurations
-                        ignite-form-group-tooltip
-                            | Service configurations
-                        ignite-form-group-add(ng-click='tableNewItem(serviceConfigurationsTbl)')
-                            | Add service configuration
-                        .group-content-empty(ng-if=`!(${model} && ${model}.length > 0)`)
-                            | Not defined
-                        .group-content(ng-show=`${model} && ${model}.length > 0` ng-repeat=`model in ${model} track by $index`)
-                            -var nodeFilter = 'model.nodeFilter';
-                            -var nodeFilterKind = nodeFilter + '.kind';
-                            -var igfsFilter = nodeFilterKind + ' === "IGFS"'
-                            -var customFilter = nodeFilterKind + ' === "Custom"'
+        .pca-panel-heading-title Service configuration
+        .pca-panel-heading-description
+            | Service Grid allows for deployments of arbitrary user-defined services on the cluster. 
+            | #[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
+                mixin clusters-service-configurations
+                    .ignite-form-field(ng-init='serviceConfigurationsTbl={type: "serviceConfigurations", model: "serviceConfigurations", focusId: "kind", ui: "failover-table"}')
+                        +ignite-form-field__label('Service configurations:', '"serviceConfigurations"')
+                        .ignite-form-field__control
+                            -let items = model
 
-                            hr(ng-if='$index != 0')
-                            .settings-row
-                                +text-enabled-autofocus('Name:', 'model.name', '"ServiceName" + $index', 'true', 'true', 'Input service name', 'Service name')
-                                    +table-remove-button(model, 'Remove service configuration')
-                            .settings-row
-                                +java-class('Service class', 'model.service', '"serviceService" + $index', 'true', 'true', 'Service implementation class name')
-                            .settings-row
-                                +number('Max per node count:', 'model.maxPerNodeCount', '"ServiceMaxPerNodeCount" + $index', 'true', 'Unlimited', '0',
-                                    'Maximum number of deployed service instances on each node.<br/>' +
-                                    'Zero for unlimited')
-                            .settings-row
-                                +number('Total count:', 'model.totalCount', '"ServiceTotalCount" + $index', 'true', 'Unlimited', '0',
-                                    'Total number of deployed service instances in the cluster.<br/>' +
-                                    'Zero for unlimited')
-                            //-
-                                .settings-row
-                                    +dropdown('Node filter:', nodeFilterKind, '"nodeFilter" + $index', 'true', 'Not set',
-                                    '[\
-                                        {value: "IGFS", label: "IGFS nodes"},\
-                                        {value: "Custom", label: "Custom"},\
-                                        {value: undefined, label: "Not set"}\
-                                    ]',
-                                    'Node filter variant'
-                                    )
-                                .panel-details(ng-show=igfsFilter)
+                            list-editable(ng-model=items name='serviceConfigurations')
+                                list-editable-item-edit
+                                    - form = '$parent.form'
+                        
+                                    -var nodeFilter = '$item.nodeFilter';
+                                    -var nodeFilterKind = nodeFilter + '.kind';
+                                    -var customFilter = nodeFilterKind + ' === "Custom"'
+
+                                    .settings-row
+                                        +sane-ignite-form-field-text({
+                                            label: 'Name:',
+                                            model: '$item.name',
+                                            name: '"serviceName"',
+                                            required: true,
+                                            placeholder: 'Input service name'
+                                        })(
+                                            ui-validate=`{
+                                                uniqueName: '$ctrl.Clusters.serviceConfigurations.serviceConfiguration.name.customValidators.uniqueName($item, ${items})'
+                                            }`
+                                            ui-validate-watch=`"${items}"`
+                                            ui-validate-watch-object-equality='true'
+                                            ng-model-options='{allowInvalid: true}'
+                                        )
+                                            +form-field-feedback('"serviceName', 'uniqueName', 'Service with that name is already configured')
+                                    .settings-row
+                                        +java-class('Service class', '$item.service', '"serviceService"', 'true', 'true', 'Service implementation class name')
+                                    .settings-row
+                                        +number('Max per node count:', '$item.maxPerNodeCount', '"ServiceMaxPerNodeCount"', 'true', 'Unlimited', '0',
+                                            'Maximum number of deployed service instances on each node.<br/>' +
+                                            'Zero for unlimited')
                                     .settings-row
-                                        +dropdown-required-empty('IGFS:', nodeFilter + '.IGFS.igfs', '"igfsNodeFilter"', 'true', igfsFilter,
-                                            'Choose IGFS', 'No IGFS configured', 'igfss', 'Select IGFS to filter nodes')
-                                .panel-details(ng-show=customFilter)
+                                        +number('Total count:', '$item.totalCount', '"serviceTotalCount"', 'true', 'Unlimited', '0',
+                                            'Total number of deployed service instances in the cluster.<br/>' +
+                                            'Zero for unlimited')
                                     .settings-row
-                                        +java-class('Class name:', nodeFilter + '.Custom.className', '"customNodeFilter"',
-                                            'true', customFilter, 'Class name of custom node filter implementation', customFilter)
-                            .settings-row
-                                +dropdown-required-empty('Cache:', 'model.cache', '"ServiceCache" + $index', 'true', 'false',
-                                    'Choose cache', 'No caches configured for current cluster', 'clusterCachesEmpty', 'Cache name used for key-to-node affinity calculation').settings-row
-                            .settings-row
-                                +text('Affinity key:', 'model.affinityKey', '"ServiceAffinityKey" + $index', 'false', 'Input affinity key',
-                                    'Affinity key used for key-to-node affinity calculation')
-            .col-sm-6
-                +preview-xml-java('backupItem', 'clusterServiceConfiguration', 'caches')
+                                        +dropdown-required-empty('Cache:', '$item.cache', '"serviceCache"', 'true', 'false',
+                                            'Choose cache', 'No caches configured for current cluster', '$ctrl.cachesMenu', 'Cache name used for key-to-node affinity calculation')(
+                                            pc-is-in-collection='$ctrl.clonedCluster.caches'
+                                        ).settings-row
+                                            +form-field-feedback(form, 'isInCollection', `Cluster doesn't have such a cache`)
+                                    .settings-row
+                                        +text('Affinity key:', '$item.affinityKey', '"serviceAffinityKey"', 'false', 'Input affinity key',
+                                            'Affinity key used for key-to-node affinity calculation')
+
+                                list-editable-no-items
+                                    list-editable-add-item-button(
+                                        add-item=`$ctrl.Clusters.addServiceConfiguration($ctrl.clonedCluster)`
+                                        label-single='service configuration'
+                                        label-multiple='service configurations'
+                                    )
+
+                +clusters-service-configurations
+
+            .pca-form-column-6
+                +preview-xml-java('$ctrl.clonedCluster', 'clusterServiceConfiguration', '$ctrl.caches')

http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/modules/states/configuration/clusters/sql-connector.pug
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/configuration/clusters/sql-connector.pug b/modules/web-console/frontend/app/modules/states/configuration/clusters/sql-connector.pug
index d72f962..b52b973 100644
--- a/modules/web-console/frontend/app/modules/states/configuration/clusters/sql-connector.pug
+++ b/modules/web-console/frontend/app/modules/states/configuration/clusters/sql-connector.pug
@@ -17,44 +17,41 @@
 include /app/helpers/jade/mixins
 
 -var form = 'query'
--var model = 'backupItem'
+-var model = '$ctrl.clonedCluster'
 -var connectionModel = model + '.sqlConnectorConfiguration'
 -var connectionEnabled = connectionModel + '.enabled'
 
-.panel.panel-default(ng-show='$ctrl.available(["2.1.0", "2.3.0"])' ng-form=form novalidate)
-    .panel-heading(bs-collapse-toggle ng-click=`ui.loadPanel('${form}')`)
+.pca-panel.pca-panel-default(ng-show='$ctrl.available(["2.1.0", "2.3.0"])' ng-form=form novalidate)
+    .pca-panel-heading(bs-collapse-toggle ng-click=`ui.loadPanel('${form}')`)
         ignite-form-panel-chevron
-        label Query configuration
-        ignite-form-field-tooltip.tipLabel
-            | Query configuration
+        .pca-panel-heading-title Query configuration
         //- TODO IGNITE-5415 Add link to documentation.
-        ignite-form-revert
-    .panel-collapse(role='tabpanel' bs-collapse-target id=`${form}`)
-        .panel-body(ng-if=`$ctrl.available(["2.1.0", "2.3.0"]) && ui.isPanelLoaded('${form}')`)
-            .col-sm-6
-                .settings-row
+    .pca-panel-collapse(role='tabpanel' bs-collapse-target id=`${form}`)
+        .pca-panel-body(ng-if=`$ctrl.available(["2.1.0", "2.3.0"]) && ui.isPanelLoaded('${form}')`).pca-form-row
+            .pca-form-column-6.pc-form-grid-row
+                .pc-form-grid-col-60
                     +checkbox('Enabled', connectionEnabled, '"SqlConnectorEnabled"', 'Flag indicating whether to configure SQL connector configuration')
-                .settings-row
-                    +text-enabled('Host:', `${connectionModel}.host`, '"SqlConnectorHost"', connectionEnabled, 'false', 'localhost', 'Host')
-                .settings-row
-                    +number('Port:', `${connectionModel}.port`, '"SqlConnectorPort"', connectionEnabled, '10800', '1025', 'Port')
-                .settings-row
-                    +number('Port range:', `${connectionModel}.portRange`, '"SqlConnectorPortRange"', connectionEnabled, '100', '0', 'Port range')
-                .settings-row
+                .pc-form-grid-col-40
+                    +text-enabled('Host:', `${connectionModel}.host`, '"SqlConnectorHost"', connectionEnabled, 'false', 'localhost')
+                .pc-form-grid-col-20
+                    +number('Port:', `${connectionModel}.port`, '"SqlConnectorPort"', connectionEnabled, '10800', '1025')
+                .pc-form-grid-col-20
+                    +number('Port range:', `${connectionModel}.portRange`, '"SqlConnectorPortRange"', connectionEnabled, '100', '0')
+                .pc-form-grid-col-20
                     +number('Socket send buffer size:', `${connectionModel}.socketSendBufferSize`, '"SqlConnectorSocketSendBufferSize"', connectionEnabled, '0', '0',
                         'Socket send buffer size.<br/>\
                         When set to <b>0</b>, operation system default will be used')
-                .settings-row
+                .pc-form-grid-col-20
                     +number('Socket receive buffer size:', `${connectionModel}.socketReceiveBufferSize`, '"SqlConnectorSocketReceiveBufferSize"', connectionEnabled, '0', '0',
                         'Socket receive buffer size.<br/>\
                         When set to <b>0</b>, operation system default will be used')
-                .settings-row
+                .pc-form-grid-col-30
                     +number('Max connection cursors:', `${connectionModel}.maxOpenCursorsPerConnection`, '"SqlConnectorMaxOpenCursorsPerConnection"', connectionEnabled, '128', '0',
                         'Max number of opened cursors per connection')
-                .settings-row
+                .pc-form-grid-col-30
                     +number('Pool size:', `${connectionModel}.threadPoolSize`, '"SqlConnectorThreadPoolSize"', connectionEnabled, 'max(8, availableProcessors)', '1',
                         'Size of thread pool that is in charge of processing SQL requests')
-                .settings-row
-                    +checkbox-enabled('TCP_NODELAY option', `${connectionModel}.tcpNoDelay`, '"SqlConnectorTcpNoDelay"', connectionEnabled, 'Value for TCP_NODELAY socket option')
-            .col-sm-6
+                .pc-form-grid-col-60
+                    +checkbox-enabled('TCP_NODELAY option', `${connectionModel}.tcpNoDelay`, '"SqlConnectorTcpNoDelay"', connectionEnabled)
+            .pca-form-column-6
                 +preview-xml-java(model, 'clusterQuery')

http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/modules/states/configuration/clusters/ssl.pug
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/configuration/clusters/ssl.pug b/modules/web-console/frontend/app/modules/states/configuration/clusters/ssl.pug
index dcb3b21..f353e2e 100644
--- a/modules/web-console/frontend/app/modules/states/configuration/clusters/ssl.pug
+++ b/modules/web-console/frontend/app/modules/states/configuration/clusters/ssl.pug
@@ -17,94 +17,77 @@
 include /app/helpers/jade/mixins
 
 -var form = 'sslConfiguration'
--var cluster = 'backupItem'
--var enabled = 'backupItem.sslEnabled'
+-var cluster = '$ctrl.clonedCluster'
+-var enabled = '$ctrl.clonedCluster.sslEnabled'
 -var model = cluster + '.sslContextFactory'
 -var trust = model + '.trustManagers'
 
-.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(id='sslConfiguration-title') SSL configuration
-        ignite-form-field-tooltip.tipLabel
-            | Settings for SSL configuration for creating a secure socket layer#[br]
-            | #[a(href="https://apacheignite.readme.io/docs/ssltls" 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
-                    +checkbox('Enabled', enabled, '"sslEnabled"', 'Flag indicating whether to configure SSL configuration')
-                .settings-row
-                    +text-options('Algorithm to create a key manager:', `${model}.keyAlgorithm`, '"keyAlgorithm"', '["SumX509", "X509"]', enabled, 'false', 'SumX509',
-                        'Sets key manager algorithm that will be used to create a key manager<br/>\
-                        Notice that in most cased default value suites well, however, on Android platform this value need to be set to X509')
-                .settings-row
-                    +text-enabled('Key store file:', `${model}.keyStoreFilePath`, '"keyStoreFilePath"', enabled, enabled, 'Path to the key store file',
-                        'Path to the key store file<br/>\
-                        This is a mandatory parameter since ssl context could not be initialized without key manager')
-                .settings-row
-                    +text-options('Key store type:', `${model}.keyStoreType`, '"keyStoreType"', '["JKS", "PCKS11", "PCKS12"]', enabled, 'false', 'JKS',
-                        'Key store type used in context initialization')
-                .settings-row
-                    +text-options('Protocol:', `${model}.protocol`, '"protocol"', '["TSL", "SSL"]', enabled, 'false', 'TSL', 'Protocol for secure transport')
-                .settings-row
-                    -var form = 'trustManagers'
+        .pca-panel-heading-title SSL configuration
+        .pca-panel-heading-description
+            | Settings for SSL configuration for creating a secure socket layer. 
+            | #[a.link-success(href="https://apacheignite.readme.io/docs/ssltls" 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
+                        +checkbox('Enabled', enabled, '"sslEnabled"', 'Flag indicating whether to configure SSL configuration')
+                    .pc-form-grid-col-60
+                        +text-options('Algorithm to create a key manager:', `${model}.keyAlgorithm`, '"keyAlgorithm"', '["SumX509", "X509"]', enabled, 'false', 'SumX509',
+                            'Sets key manager algorithm that will be used to create a key manager<br/>\
+                            Notice that in most cased default value suites well, however, on Android platform this value need to be set to X509')
+                    .pc-form-grid-col-60
+                        +text-enabled('Key store file:', `${model}.keyStoreFilePath`, '"keyStoreFilePath"', enabled, enabled, 'Path to the key store file',
+                            'Path to the key store file<br/>\
+                            This is a mandatory parameter since ssl context could not be initialized without key manager')
+                    .pc-form-grid-col-30
+                        +text-options('Key store type:', `${model}.keyStoreType`, '"keyStoreType"', '["JKS", "PCKS11", "PCKS12"]', enabled, 'false', 'JKS',
+                            'Key store type used in context initialization')
+                    .pc-form-grid-col-30
+                        +text-options('Protocol:', `${model}.protocol`, '"protocol"', '["TSL", "SSL"]', enabled, 'false', 'TSL', 'Protocol for secure transport')
+                    .pc-form-grid-col-60
+                        .ignite-form-field
+                            .ignite-form-field__control
+                                list-editable(
+                                    ng-model=trust
+                                    name='trustManagers'
+                                    list-editable-cols=`::[{name: "Pre-configured trust managers:"}]`
+                                    ng-disabled=enabledToDisabled(enabled)
+                                    ng-required=`${enabled} && !${model}.trustStoreFilePath`
+                                )
+                                    list-editable-item-view {{ $item }}
 
-                    +ignite-form-group(ng-form=form ng-model=trust)
-                        -var uniqueTip = 'Such trust manager already exists!'
+                                    list-editable-item-edit
+                                        +list-java-class-field('Trust manager', '$item', '"trustManager"', trust)
+                                            +unique-feedback('"trustManager"', 'Such trust manager already exists!')
 
-                        ignite-form-field-label
-                            | Trust managers
-                        ignite-form-group-tooltip
-                            | Pre-configured trust managers
-                        ignite-form-group-add(ng-show=`${enabled}` ng-click='(group.add = [{}])')
-                            | Add new trust manager
+                                    list-editable-no-items
+                                        list-editable-add-item-button(
+                                            add-item=`$editLast((${trust} = ${trust} || []).push(''))`
+                                            label-single='trust manager'
+                                            label-multiple='trust managers'
+                                        )
+                            .ignite-form-field__errors(
+                                ng-messages=`sslConfiguration.trustManagers.$error`
+                                ng-show=`sslConfiguration.trustManagers.$invalid`
+                            )
+                                +form-field-feedback(_, 'required', 'Trust managers or trust store file should be configured')
 
-                        .group-content(ng-if=`${trust}.length`)
-                            -var model = 'obj.model';
-                            -var name = '"edit" + $index'
-                            -var valid = `${form}[${name}].$valid`
-                            -var save = `${trust}[$index] = ${model}`
-                
-                            div(ng-show=enabled)
-                                div(ng-repeat=`model in ${trust} track by $index` ng-init='obj = {}')
-                                    label.col-xs-12.col-sm-12.col-md-12
-                                        .indexField
-                                            | {{ $index+1 }})
-                                        +table-remove-conditional-button(trust, enabled, 'Remove trust manager', 'model')
-                                        span(ng-hide='field.edit')
-                                            a.labelFormField(ng-click=`${enabled} && (field.edit = true) && (${model} = model)`) {{ model }}
-                                        span(ng-if='field.edit')
-                                            +table-java-class-field('Trust manager', name, model, trust, valid, save, false)
-                                                +table-save-button(valid, save, false)
-                                                +unique-feedback(name, uniqueTip)
-                            div(ng-hide=enabled)
-                                div(ng-repeat=`model in ${trust} 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 = `${trust}.push(${model})`
- 
-                            div
-                                label.col-xs-12.col-sm-12.col-md-12
-                                    +table-java-class-field('Trust manager', name, model, trust, valid, save, true)
-                                        +table-save-button(valid, save, true)
-                                        +unique-feedback(name, uniqueTip)
-
-                        .group-content-empty(ng-if=`!(${trust}.length) && !group.add.length`)
-                            | Not defined
-
-                .settings-row(ng-show=`!${trust}.length`)
-                    +text-enabled('Trust store file:', `${model}.trustStoreFilePath`, '"trustStoreFilePath"', enabled, 'false', 'Path to the trust store file', 'Path to the trust store file')
-                .settings-row(ng-show=`!${trust}.length`)
-                    +text-options('Trust store type:', `${model}.trustStoreType`, '"trustStoreType"', '["JKS", "PCKS11", "PCKS12"]', enabled, 'false', 'JKS', 'Trust store type used in context initialization')
-            .col-sm-6
+                    .pc-form-grid-col-30(ng-if-start=`!${trust}.length`)
+                        +sane-ignite-form-field-text({
+                            label: 'Trust store file:',
+                            model: `${model}.trustStoreFilePath`,
+                            name: '"trustStoreFilePath"',
+                            required: `${enabled} && !${trust}.length`,
+                            disabled: enabledToDisabled(enabled),
+                            placeholder: 'Path to the trust store file',
+                            tip: 'Path to the trust store file'
+                        })
+                            +form-field-feedback(_, 'required', 'Trust store file or trust managers should be configured')
+                    .pc-form-grid-col-30(ng-if-end)
+                        +text-options('Trust store type:', `${model}.trustStoreType`, '"trustStoreType"', '["JKS", "PCKS11", "PCKS12"]', enabled, 'false', 'JKS', 'Trust store type used in context initialization')
+            .pca-form-column-6
                 +preview-xml-java(cluster, 'clusterSsl')

http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/modules/states/configuration/clusters/swap.pug
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/configuration/clusters/swap.pug b/modules/web-console/frontend/app/modules/states/configuration/clusters/swap.pug
index 60226cd..e44e6a1 100644
--- a/modules/web-console/frontend/app/modules/states/configuration/clusters/swap.pug
+++ b/modules/web-console/frontend/app/modules/states/configuration/clusters/swap.pug
@@ -17,56 +17,57 @@
 include /app/helpers/jade/mixins
 
 -var form = 'swap'
--var model = 'backupItem'
+-var model = '$ctrl.clonedCluster'
 -var swapModel = model + '.swapSpaceSpi'
 -var fileSwapModel = swapModel + '.FileSwapSpaceSpi'
 
-.panel.panel-default(ng-show='$ctrl.available(["1.0.0", "2.0.0"])' ng-form=form novalidate)
-    .panel-heading(bs-collapse-toggle ng-click=`ui.loadPanel('${form}')`)
+.pca-panel.pca-panel-default(ng-show='$ctrl.available(["1.0.0", "2.0.0"])' ng-form=form novalidate)
+    .pca-panel-heading(bs-collapse-toggle ng-click=`ui.loadPanel('${form}')`)
         ignite-form-panel-chevron
-        label Swap
-        ignite-form-field-tooltip.tipLabel
-            | Settings for overflow data to disk if it cannot fit in memory#[br]
-            | #[a(href="https://apacheignite.readme.io/v1.9/docs/off-heap-memory#swap-space" target="_blank") More info]
-        ignite-form-revert
-    .panel-collapse(role='tabpanel' bs-collapse-target id=`${form}`)
-        .panel-body(ng-if=`$ctrl.available(["1.0.0", "2.0.0"]) && ui.isPanelLoaded('${form}')`)
-            .col-sm-6
-                .settings-row
+        .pca-panel-heading-title Swap
+        .pca-panel-heading-description
+            | Settings for overflow data to disk if it cannot fit in memory. 
+            | #[a.link-success(href="https://apacheignite.readme.io/v1.9/docs/off-heap-memory#swap-space" target="_blank") More info]
+    .pca-panel-collapse(role='tabpanel' bs-collapse-target id=`${form}`)
+        .pca-panel-body(ng-if=`$ctrl.available(["1.0.0", "2.0.0"]) && ui.isPanelLoaded('${form}')`).pca-form-row
+            .pca-form-column-6.pc-form-grid-row
+                .pc-form-grid-col-60
                     +dropdown('Swap space SPI:', `${swapModel}.kind`, '"swapSpaceSpi"', 'true', 'Choose swap SPI',
-                        '[\
-                            {value: "FileSwapSpaceSpi", label: "File-based swap"},\
-                            {value: null, label: "Not set"}\
-                        ]',
+                        '::$ctrl.Clusters.swapSpaceSpis',
                         'Provides a mechanism in grid for storing data on disk<br/>\
                         Ignite cache uses swap space to overflow data to disk if it cannot fit in memory\
                         <ul>\
                             <li>File-based swap - File-based swap space SPI implementation which holds keys in memory</li>\
                             <li>Not set - File-based swap space SPI with default configuration when it needed</li>\
                         </ul>')
-                    a.customize(
-                        ng-if=`${swapModel}.kind`
-                        ng-click=`${swapModel}.expanded = !${swapModel}.expanded`
-                    ) {{ #{swapModel}.expanded ? 'Hide settings' : 'Show settings'}}
-                .settings-row
-                    .panel-details(ng-show=`${swapModel}.expanded && ${swapModel}.kind`)
-                        .details-row
-                            +text('Base directory:', `${fileSwapModel}.baseDirectory`, '"baseDirectory"', 'false', 'swapspace',
-                                'Base directory where to write files')
-                        .details-row
-                            +number('Read stripe size:', `${fileSwapModel}.readStripesNumber`, '"readStripesNumber"', 'true', 'availableProcessors', '0',
-                                'Read stripe size defines number of file channels to be used concurrently')
-                        .details-row
-                            +number-min-max-step('Maximum sparsity:', `${fileSwapModel}.maximumSparsity`, '"maximumSparsity"', 'true', '0.5', '0', '0.999', '0.05',
-                                'This property defines maximum acceptable wasted file space to whole file size ratio<br/>\
-                                When this ratio becomes higher than specified number compacting thread starts working')
-                        .details-row
-                            +number('Max write queue size:', `${fileSwapModel}.maxWriteQueueSize`, '"maxWriteQueueSize"', 'true', '1024 * 1024', '0',
-                                'Max write queue size in bytes<br/>\
-                                If there are more values are waiting for being written to disk then specified size, SPI will block on store operation')
-                        .details-row
-                            +number('Write buffer size:', `${fileSwapModel}.writeBufferSize`, '"writeBufferSize"', 'true', '64 * 1024', '0',
-                                'Write buffer size in bytes<br/>\
-                                Write to disk occurs only when this buffer is full')
-            .col-sm-6
+                .pc-form-group.pc-form-grid-row(ng-show=`${swapModel}.kind`)
+                    .pc-form-grid-col-60
+                        +text('Base directory:', `${fileSwapModel}.baseDirectory`, '"baseDirectory"', 'false', 'swapspace',
+                            'Base directory where to write files')
+                    .pc-form-grid-col-30
+                        +sane-ignite-form-field-number({
+                            label: 'Read stripe size:',
+                            model: `${fileSwapModel}.readStripesNumber`,
+                            name: '"readStripesNumber"',
+                            placeholder: '{{ ::$ctrl.Clusters.swapSpaceSpi.readStripesNumber.default }}',
+                            tip: 'Read stripe size defines number of file channels to be used concurrently'
+                        })(
+                            ui-validate=`{
+                                powerOfTwo: '$ctrl.Clusters.swapSpaceSpi.readStripesNumber.customValidators.powerOfTwo($value)'
+                            }`
+                        )
+                            +form-field-feedback('"readStripesNumber"', 'powerOfTwo', 'Read stripe size must be positive and power of two')
+                    .pc-form-grid-col-30
+                        +number-min-max-step('Maximum sparsity:', `${fileSwapModel}.maximumSparsity`, '"maximumSparsity"', 'true', '0.5', '0', '0.999', '0.05',
+                            'This property defines maximum acceptable wasted file space to whole file size ratio<br/>\
+                            When this ratio becomes higher than specified number compacting thread starts working')
+                    .pc-form-grid-col-30
+                        +number('Max write queue size:', `${fileSwapModel}.maxWriteQueueSize`, '"maxWriteQueueSize"', 'true', '1024 * 1024', '0',
+                            'Max write queue size in bytes<br/>\
+                            If there are more values are waiting for being written to disk then specified size, SPI will block on store operation')
+                    .pc-form-grid-col-30
+                        +number('Write buffer size:', `${fileSwapModel}.writeBufferSize`, '"writeBufferSize"', 'true', '64 * 1024', '0',
+                            'Write buffer size in bytes<br/>\
+                            Write to disk occurs only when this buffer is full')
+            .pca-form-column-6
                 +preview-xml-java(model, 'clusterSwap')

http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/modules/states/configuration/clusters/thread.pug
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/configuration/clusters/thread.pug b/modules/web-console/frontend/app/modules/states/configuration/clusters/thread.pug
index 8298c09..4efa987 100644
--- a/modules/web-console/frontend/app/modules/states/configuration/clusters/thread.pug
+++ b/modules/web-console/frontend/app/modules/states/configuration/clusters/thread.pug
@@ -17,76 +17,132 @@
 include /app/helpers/jade/mixins
 
 -var form = 'pools'
--var model = 'backupItem'
+-var model = '$ctrl.clonedCluster'
 -var executors = model + '.executorConfiguration'
 
-.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 Thread pools size
-        ignite-form-field-tooltip.tipLabel
-            | Settings for node thread pools
-        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 Thread pools size
+        .pca-panel-heading-description
+            | Settings for node thread pools.
+    .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-30
                     +number('Public:', model + '.publicThreadPoolSize', '"publicThreadPoolSize"', 'true', 'max(8, availableProcessors) * 2', '1',
                         'Thread pool that is in charge of processing ComputeJob, GridJobs and user messages sent to node')
-                .settings-row
-                    +number('System:', model + '.systemThreadPoolSize', '"systemThreadPoolSize"', 'true', 'max(8, availableProcessors) * 2', '1',
-                        'Thread pool that is in charge of processing internal system messages')
-                .settings-row
+                .pc-form-grid-col-30
+                    +sane-ignite-form-field-number({
+                        label: 'System:',
+                        model: `${model}.systemThreadPoolSize`,
+                        name: '"systemThreadPoolSize"',
+                        placeholder: '{{ ::$ctrl.Clusters.systemThreadPoolSize.default }}',
+                        min: '{{ ::$ctrl.Clusters.systemThreadPoolSize.min }}',
+                        tip: 'Thread pool that is in charge of processing internal system messages'
+                    })
+                .pc-form-grid-col-30
                     +number('Service:', model + '.serviceThreadPoolSize', '"serviceThreadPoolSize"', 'true', 'max(8, availableProcessors) * 2', '1',
                         'Thread pool that is in charge of processing proxy invocation')
-                .settings-row
+                .pc-form-grid-col-30
                     +number('Management:', model + '.managementThreadPoolSize', '"managementThreadPoolSize"', 'true', '4', '1',
                         'Thread pool that is in charge of processing internal and Visor ComputeJob, GridJobs')
-                .settings-row
+                .pc-form-grid-col-30
                     +number('IGFS:', model + '.igfsThreadPoolSize', '"igfsThreadPoolSize"', 'true', 'availableProcessors', '1',
                         'Thread pool that is in charge of processing outgoing IGFS messages')
-                .settings-row
-                    +number('Rebalance:', model + '.rebalanceThreadPoolSize', '"rebalanceThreadPoolSize"', 'true', '1', '1',
-                        'Max count of threads can be used at rebalancing')
-                .settings-row
+                .pc-form-grid-col-30
+                    +sane-ignite-form-field-number({
+                        label: 'Rebalance:',
+                        model: `${model}.rebalanceThreadPoolSize`,
+                        name: '"rebalanceThreadPoolSize"',
+                        placeholder: '{{ ::$ctrl.Clusters.rebalanceThreadPoolSize.default }}',
+                        min: '{{ ::$ctrl.Clusters.rebalanceThreadPoolSize.min }}',
+                        max: `{{ $ctrl.Clusters.rebalanceThreadPoolSize.max(${model}) }}`,
+                        tip: 'Max count of threads can be used at rebalancing'
+                    })
+                        +form-field-feedback('max', 'Rebalance thread pool size should not exceed or be equal to System thread pool size')
+                .pc-form-grid-col-30
                     +number('Utility cache:', model + '.utilityCacheThreadPoolSize', '"utilityCacheThreadPoolSize"', 'true', 'max(8, availableProcessors)', '1',
                         'Default thread pool size that will be used to process utility cache messages')
-                .settings-row
-                    +number('Utility cache keep alive time:', model + '.utilityCacheKeepAliveTime', '"utilityCacheKeepAliveTime"', 'true', '60000', '0',
-                        'Keep alive time of thread pool size that will be used to process utility cache messages')
-                .settings-row
+                .pc-form-grid-col-30
+                    pc-form-field-size(
+                        label='Utility cache keep alive time:'
+                        ng-model=`${model}.utilityCacheKeepAliveTime`
+                        name='utilityCacheKeepAliveTime'
+                        size-type='seconds'
+                        size-scale-label='s'
+                        tip='Keep alive time of thread pool size that will be used to process utility cache messages'
+                        min='0'
+                        placeholder='{{ 60000 / _s1.value }}'
+                        on-scale-change='_s1 = $event'
+                    )
+                .pc-form-grid-col-30
                     +number('Async callback:', model + '.asyncCallbackPoolSize', '"asyncCallbackPoolSize"', 'true', 'max(8, availableProcessors)', '1',
                         'Size of thread pool that is in charge of processing asynchronous callbacks')
-                .settings-row
+                .pc-form-grid-col-30
                     +number('Striped:', model + '.stripedPoolSize', '"stripedPoolSize"', 'true', 'max(8, availableProcessors)', '1',
                         'Striped pool size that should be used for cache requests processing')
 
                 //- Since ignite 2.0
-                div(ng-if='$ctrl.available("2.0.0")')
-                    .settings-row
-                        +number('Data streamer:', model + '.dataStreamerThreadPoolSize', '"dataStreamerThreadPoolSize"', 'true', 'max(8, availableProcessors)', '1',
-                            'Size of thread pool that is in charge of processing data stream messages')
-                    .settings-row
-                        +number('Query:', model + '.queryThreadPoolSize', '"queryThreadPoolSize"', 'true', 'max(8, availableProcessors)', '1',
-                            'Size of thread pool that is in charge of processing query messages')
-                    .settings-row(ng-init='executorConfigurationsTbl={type: "executorConfigurations", model: "executorConfigurations", focusId: "kind", ui: "failover-table"}')
-                        +ignite-form-group()
-                            ignite-form-field-label
-                                | Executor configurations
-                            ignite-form-group-tooltip
-                                | Custom thread pool configurations for compute tasks
-                            ignite-form-group-add(ng-click='tableNewItem(executorConfigurationsTbl)')
-                                | Add executor configuration
-                            .group-content-empty(ng-if=`!(${executors} && ${executors}.length > 0)`)
-                                | Not defined
-                            .group-content(ng-show=`${executors} && ${executors}.length > 0` ng-repeat=`model in ${executors} track by $index`)
-                                hr(ng-if='$index != 0')
-                                .settings-row
-                                    +text-enabled-autofocus('Name:', 'model.name', '"ExecutorName" + $index', 'true', 'true', 'Input executor name', 'Thread pool name')
-                                        +table-remove-button(executors, 'Remove executor configuration')
-                                .settings-row
-                                    +number('Pool size:', 'model.size', '"ExecutorPoolSize" + $index', 'true', 'max(8, availableProcessors)', '1',
-                                        'Thread pool size')
+                .pc-form-grid-col-30(ng-if-start='$ctrl.available("2.0.0")')
+                    +number('Data streamer:', model + '.dataStreamerThreadPoolSize', '"dataStreamerThreadPoolSize"', 'true', 'max(8, availableProcessors)', '1',
+                        'Size of thread pool that is in charge of processing data stream messages')
+                .pc-form-grid-col-30
+                    +number('Query:', model + '.queryThreadPoolSize', '"queryThreadPoolSize"', 'true', 'max(8, availableProcessors)', '1',
+                        'Size of thread pool that is in charge of processing query messages')
+                .pc-form-grid-col-60(ng-if-end)
+                    .ignite-form-field
+                        +ignite-form-field__label('Executor configurations:', '"executorConfigurations"')
+                            +tooltip(`Custom thread pool configurations for compute tasks`)
+                        .ignite-form-field__control
+                            list-editable(
+                                ng-model=executors
+                                ng-model-options='{allowInvalid: true}'
+                                name='executorConfigurations'
+                                ui-validate=`{
+                                    allNamesExist: '$ctrl.Clusters.executorConfigurations.allNamesExist($value)',
+                                    allNamesUnique: '$ctrl.Clusters.executorConfigurations.allNamesUnique($value)'
+                                }`
+                            )
+                                list-editable-item-view
+                                    | {{ $item.name }} / 
+                                    | {{ $item.size || 'max(8, availableProcessors)'}}
 
-            .col-sm-6
+                                list-editable-item-edit
+                                    .pc-form-grid-row
+                                        .pc-form-grid-col-30
+                                            +sane-ignite-form-field-text({
+                                                label: 'Name:',
+                                                model: '$item.name',
+                                                name: '"ExecutorName"',
+                                                required: true,
+                                                placeholder: 'Input executor name',
+                                                tip: 'Thread pool name'
+                                            })(
+                                                ui-validate=`{
+                                                    uniqueName: '$ctrl.Clusters.executorConfiguration.name.customValidators.uniqueName($item, ${executors})'
+                                                }`
+                                                ui-validate-watch=`"${executors}"`
+                                                ui-validate-watch-object-equality='true'
+                                                ng-model-options='{allowInvalid: true}'
+                                                data-ignite-form-field-input-autofocus='true'
+                                            )
+                                                +form-field-feedback(null, 'uniqueName', 'Service with that name is already configured')
+                                        .pc-form-grid-col-30
+                                            +number('Pool size:', '$item.size', '"ExecutorPoolSize"', 'true', 'max(8, availableProcessors)', '1', 'Thread pool size')
+
+                                list-editable-no-items
+                                    list-editable-add-item-button(
+                                        add-item=`$edit($ctrl.Clusters.addExecutorConfiguration(${model}))`
+                                        label-single='executor configuration'
+                                        label-multiple='executor configurations'
+                                    )
+                        .ignite-form-field__errors(
+                            ng-messages=`pools.executorConfigurations.$error`
+                            ng-show=`pools.executorConfigurations.$invalid`
+                        )
+                            +form-field-feedback(_, 'allNamesExist', 'All executor configurations should have a name')
+                            +form-field-feedback(_, 'allNamesUnique', 'All executor configurations should have a unique name')
+
+            .pca-form-column-6
                 +preview-xml-java(model, 'clusterPools')

http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/modules/states/configuration/clusters/time.pug
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/configuration/clusters/time.pug b/modules/web-console/frontend/app/modules/states/configuration/clusters/time.pug
index 329d7c4..61c9155 100644
--- a/modules/web-console/frontend/app/modules/states/configuration/clusters/time.pug
+++ b/modules/web-console/frontend/app/modules/states/configuration/clusters/time.pug
@@ -17,34 +17,32 @@
 include /app/helpers/jade/mixins
 
 -var form = 'time'
--var model = 'backupItem'
+-var model = '$ctrl.clonedCluster'
 
-.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 Time configuration
-        ignite-form-field-tooltip.tipLabel
-            | Time settings for CLOCK write ordering mode
-        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 Time configuration
+        .pca-panel-heading-description
+            | Time settings for CLOCK write ordering mode.
+    .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
                 //- Removed in ignite 2.0
-                div(ng-if='$ctrl.available(["1.0.0", "2.0.0"])')
-                    .settings-row
-                        +number('Samples size:', `${model}.clockSyncSamples`, '"clockSyncSamples"', 'true', '8', '0',
-                            'Number of samples used to synchronize clocks between different nodes<br/>\
-                            Clock synchronization is used for cache version assignment in CLOCK order mode')
-                    .settings-row
-                        +number('Frequency:', `${model}.clockSyncFrequency`, '"clockSyncFrequency"', 'true', '120000', '0',
-                            'Frequency at which clock is synchronized between nodes, in milliseconds<br/>\
-                            Clock synchronization is used for cache version assignment in CLOCK order mode')
+                .pc-form-grid-col-30(ng-if-start='$ctrl.available(["1.0.0", "2.0.0"])')
+                    +number('Samples size:', `${model}.clockSyncSamples`, '"clockSyncSamples"', 'true', '8', '0',
+                        'Number of samples used to synchronize clocks between different nodes<br/>\
+                        Clock synchronization is used for cache version assignment in CLOCK order mode')
+                .pc-form-grid-col-30(ng-if-end)
+                    +number('Frequency:', `${model}.clockSyncFrequency`, '"clockSyncFrequency"', 'true', '120000', '0',
+                        'Frequency at which clock is synchronized between nodes, in milliseconds<br/>\
+                        Clock synchronization is used for cache version assignment in CLOCK order mode')
 
-                .settings-row
+                .pc-form-grid-col-30
                     +number-min-max('Port base:', `${model}.timeServerPortBase`, '"timeServerPortBase"', 'true', '31100', '0', '65535',
                         'Time server provides clock synchronization between nodes<br/>\
                         Base UPD port number for grid time server. Time server will be started on one of free ports in range')
-                .settings-row
+                .pc-form-grid-col-30
                     +number('Port range:', `${model}.timeServerPortRange`, '"timeServerPortRange"', 'true', '100', '1', 'Time server port range')
-            .col-sm-6
+            .pca-form-column-6
                 +preview-xml-java(model, 'clusterTime')

http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/modules/states/configuration/clusters/transactions.pug
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/configuration/clusters/transactions.pug b/modules/web-console/frontend/app/modules/states/configuration/clusters/transactions.pug
index f60589f..c888174 100644
--- a/modules/web-console/frontend/app/modules/states/configuration/clusters/transactions.pug
+++ b/modules/web-console/frontend/app/modules/states/configuration/clusters/transactions.pug
@@ -17,20 +17,19 @@
 include /app/helpers/jade/mixins
 
 -var form = 'transactions'
--var model = 'backupItem.transactionConfiguration'
+-var model = '$ctrl.clonedCluster.transactionConfiguration'
 
-.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 Transactions
-        ignite-form-field-tooltip.tipLabel
-            | Settings for transactions#[br]
-            | #[a(href="https://apacheignite.readme.io/docs/transactions" 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 Transactions
+        .pca-panel-heading-description
+            | Settings for transactions. 
+            | #[a.link-success(href="https://apacheignite.readme.io/docs/transactions" 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-30
                     +dropdown('Concurrency:', `${model}.defaultTxConcurrency`, '"defaultTxConcurrency"', 'true', 'PESSIMISTIC',
                         '[\
                             {value: "OPTIMISTIC", label: "OPTIMISTIC"},\
@@ -41,7 +40,7 @@ include /app/helpers/jade/mixins
                             <li>OPTIMISTIC - All cache operations are not distributed to other nodes until commit is called</li>\
                             <li>PESSIMISTIC - A lock is acquired on all cache operations with exception of read operations in READ_COMMITTED mode</li>\
                         </ul>')
-                .settings-row
+                .pc-form-grid-col-30
                     +dropdown('Isolation:', `${model}.defaultTxIsolation`, '"defaultTxIsolation"', 'true', 'REPEATABLE_READ',
                         '[\
                             {value: "READ_COMMITTED", label: "READ_COMMITTED"},\
@@ -54,16 +53,16 @@ include /app/helpers/jade/mixins
                             <li>REPEATABLE_READ - If a value was read once within transaction, then all consecutive reads will provide the same in-transaction value</li>\
                             <li>SERIALIZABLE - All transactions occur in a completely isolated fashion, as if all transactions in the system had executed serially, one after the other.</li>\
                         </ul>')
-                .settings-row
+                .pc-form-grid-col-60
                     +number('Default timeout:', `${model}.defaultTxTimeout`, '"defaultTxTimeout"', 'true', '0', '0', 'Default transaction timeout')
-                .settings-row
+                .pc-form-grid-col-30
                     +number('Pessimistic log cleanup delay:', `${model}.pessimisticTxLogLinger`, '"pessimisticTxLogLinger"', 'true', '10000', '0',
                         'Delay, in milliseconds, after which pessimistic recovery entries will be cleaned up for failed node')
-                .settings-row
+                .pc-form-grid-col-30
                     +number('Pessimistic log size:', `${model}.pessimisticTxLogSize`, '"pessimisticTxLogSize"', 'true', '0', '0',
                         'Size of pessimistic transactions log stored on node in order to recover transaction commit if originating node has left grid before it has sent all messages to transaction nodes')
-                .settings-row
+                .pc-form-grid-col-60
                     +java-class('Manager factory:', `${model}.txManagerFactory`, '"txManagerFactory"', 'true', 'false',
                         'Class name of transaction manager factory for integration with JEE app servers')
-            .col-sm-6
+            .pca-form-column-6
                 +preview-xml-java(model, 'clusterTransactions')

http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/modules/states/configuration/domains/general.pug
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/configuration/domains/general.pug b/modules/web-console/frontend/app/modules/states/configuration/domains/general.pug
index 7c8de9a..0238972 100644
--- a/modules/web-console/frontend/app/modules/states/configuration/domains/general.pug
+++ b/modules/web-console/frontend/app/modules/states/configuration/domains/general.pug
@@ -20,33 +20,41 @@ include /app/helpers/jade/mixins
 -var model = 'backupItem'
 -var generatePojo = `${model}.generatePojo`
 
-.panel.panel-default(ng-form=form novalidate)
-    .panel-heading(bs-collapse-toggle)
+.pca-panel.pca-panel-default(ng-form=form novalidate)
+    .pca-panel-heading(bs-collapse-toggle)
         ignite-form-panel-chevron
-        label General
-        ignite-form-field-tooltip.tipLabel
-            | Domain model properties common for Query and Store#[br]
-            | #[a(href="https://apacheignite.readme.io/docs/cache-queries" target="_blank") More info about query configuration]#[br]
-            | #[a(href="https://apacheignite.readme.io/docs/3rd-party-store" target="_blank") More info about store]
-        ignite-form-revert
-    .panel-collapse(role='tabpanel' bs-collapse-target id=`${form}`)
-        .panel-body
-            .col-sm-6
-                .settings-row
+        .pca-panel-heading-title General
+        .pca-panel-heading-description
+            | Domain model properties common for Query and Store. 
+            a.link-success(href="https://apacheignite.readme.io/docs/cache-queries" target="_blank") More info about query configuration. 
+            a.link-success(href="https://apacheignite.readme.io/docs/3rd-party-store" target="_blank") More info about store.
+    .pca-panel-collapse(role='tabpanel' bs-collapse-target id=`${form}`)
+        .pca-panel-body.pca-form-row
+            .pca-form-column-6.pc-form-grid-row
+                .pc-form-grid-col-60
                     +checkbox('Generate POJO classes', generatePojo, '"generatePojo"', 'If selected then POJO classes will be generated from database tables')
-                .settings-row
-                    +caches(model, 'Select caches to associate domain model with cache')
-                .settings-row
-                    +dropdown-required('Query metadata:', `${model}.queryMetadata`, '"queryMetadata"', 'true', 'true', '', 'queryMetadataVariants',
+                .pc-form-grid-col-30
+                    +sane-ignite-form-field-dropdown({
+                        label: 'Caches:',
+                        model: `${model}.caches`,
+                        name: '"caches"',
+                        multiple: true,
+                        placeholder: 'Choose caches',
+                        placeholderEmpty: 'No valid caches configured',
+                        options: '$ctrl.cachesMenu',
+                        tip: 'Select caches to describe types in cache'
+                    })
+                .pc-form-grid-col-30
+                    +dropdown-required('Query metadata:', `${model}.queryMetadata`, '"queryMetadata"', 'true', 'true', '', '::$ctrl.Models.queryMetadata.values',
                         'Query metadata configured with:\
                         <ul>\
                             <li>Java annotations like @QuerySqlField</li>\
                             <li>Configuration via QueryEntity class</li>\
                         </ul>')
-                .settings-row
-                    +java-class-typeahead('Key type:', `${model}.keyType`, '"keyType"', 'javaBuiltInClassesBase', 'true', 'true', '{{ ' + generatePojo + ' ? "Full class name for Key" : "Key type name" }}', 'Key class used to store key in cache', generatePojo)
-                .settings-row
+                .pc-form-grid-col-60
+                    +java-class-typeahead('Key type:', `${model}.keyType`, '"keyType"', '$ctrl.javaBuiltInClassesBase', 'true', 'true', '{{ ' + generatePojo + ' ? "Full class name for Key" : "Key type name" }}', 'Key class used to store key in cache', generatePojo)
+                .pc-form-grid-col-60
                     +java-class-autofocus-placholder('Value type:', `${model}.valueType`, '"valueType"', 'true', 'true', 'false', '{{ ' + generatePojo +' ? "Enter fully qualified class name" : "Value type name" }}', 'Value class used to store value in cache', generatePojo)
 
-            .col-sm-6
+            .pca-form-column-6
                 +preview-xml-java(model, 'domainModelGeneral')