浏览代码

updated TableAjax wps actions for selected features

Piotr Labudda 8 年之前
父节点
当前提交
7677d13dec
共有 2 个文件被更改,包括 263 次插入96 次删除
  1. 1 0
      SE/se-lib/TableAjax.php
  2. 262 96
      SE/se-lib/TableAjax.php.TableAjax.js

+ 1 - 0
SE/se-lib/TableAjax.php

@@ -420,6 +420,7 @@ class TableAjax extends ViewAjax {
 		UI::inlineJS(APP_PATH_WWW . '/static/p5UI/FieldCheckboxLoading.js', []); // p5UI__FieldCheckboxLoading
 		UI::inlineJS(__FILE__ . '.TableAjax.js', [
 			'URI_BASE' => Request::getPathUri(),
+			'URI_WPS' => Request::getPathUri() . 'wps.php',
 			'DBG' => ('1' === V::get('DBG_JS', '', $_GET)),
 		]);
 		$filterInit = $this->_filterInit;

+ 262 - 96
SE/se-lib/TableAjax.php.TableAjax.js

@@ -1,4 +1,5 @@
 if (!URI_BASE) throw "Missing URI_BASE"; // => Request::getPathUri()
+if (!URI_WPS) throw "Missing URI_WPS";
 if (!global.jQuery) throw "Missing jQuery"
 var $ = global.jQuery
 var DBG = DBG || false;
@@ -11,168 +12,261 @@ var ReduxThunk = global.p5VendorJs.ReduxThunk;
 var createStoreWithThunkMiddleware = Redux.applyMiddleware(ReduxThunk)(Redux.createStore); // TODO: to vendor.js
 var p5UI__FieldCheckboxSearch = global.p5VendorJs['p5UI__FieldCheckboxSearch'];
 var p5UI__FieldCheckboxLoading = global.p5VendorJs['p5UI__FieldCheckboxLoading'];
+var p5WPSHelper_generateProcessSelectFeatureWithArgs = function (identifier, typeName, listPrimaryKeys) {
+	return {
+		identifier: identifier, // "ows:Identifier"
+		dataInputs: [ // "wps:DataInputs"
+			{ // "wps:Input"
+				identifier: "typeName", // "ows:Identifier"
+				// title: "Feature typeName", // "ows:Title"
+				data: { literalData: { value: typeName } }, // "wps:Data"
+				// reference: "???", // "wps:Reference"
+				// boundingBoxData: "???", // "wps:BoundingBoxData"
+			}
+		].concat(
+			listPrimaryKeys.map(function (pk) {
+				return { // "wps:Input"
+					identifier: "primaryKey", // "ows:Identifier"
+					// title: "Feature typeName", // "ows:Title"
+					data: { literalData: { value: pk } }, // "wps:Data"
+					// reference: "???", // "wps:Reference"
+					// boundingBoxData: "???", // "wps:BoundingBoxData"
+				};
+			})
+		),
+		responseForm: { // "wps:ResponseForm"
+			// rawDataOutput: "???", // "wps:RawDataOutput"
+			// responseDocument: "???", // "wps:ResponseDocument"
+		}
+	}
+}
+var p5WPS_executeFeatureProcess = function(identifier) {
+	return function (ns, listPrimaryKeys) {
+		return new Promise(function (resolve, reject) {
+			var process = p5WPSHelper_generateProcessSelectFeatureWithArgs(identifier, ns, listPrimaryKeys);
+			OpenLayers.Request.POST({
+				url: URI_WPS,
+				data: new OpenLayers.Format.WPSExecute().write(process),
+				callback: function (response) {
+					try {
+						var txt = response.responseText || ''
+						if (!txt) throw "Empty response";
+						var responseJson = JSON.parse(txt)
+						if ('success' != responseJson.type) throw "Error response type '" + responseJson.type + "'";
+						if (!responseJson.body || !responseJson.body.selected) throw "Brak danych";
+						var listSelectedPks = Object.keys(responseJson.body.selected).filter(function (pk) {
+							return responseJson.body.selected[pk];
+						})
+						var totalSelected = responseJson.body.totalSelected;
+						DBG && console.log('wps('+identifier+') response', {response, responseJson, selected: responseJson.body.selected, resolve: listSelectedPks});
+
+						resolve({
+							selected: listSelectedPks,
+							totalSelected: totalSelected,
+						})
+					} catch (e) {
+						reject('' + e)
+						return;
+					}
+				},
+				// success: function (response) {
+				// 	console.log('response', response);
+				// },
+				// failure: function (response) {
+				// 	console.log('response', response);
+				// }
+			});
+		});
+	}
+};
+var p5WPS__selecFeature = p5WPS_executeFeatureProcess('p5:selectFeature');
+var p5WPS__unselectFeature = p5WPS_executeFeatureProcess('p5:unselectFeature');
+var p5WPS__unselecAllFeatures = p5WPS_executeFeatureProcess('p5:unselectAllFeatures');
+// var p5WPS__selecAllFeatures = p5WPS_executeFeatureProcess('p5:selectAllFeatures'); // TODO: send current table ogc filter
+var p5WPS__getSelectedFeatures = p5WPS_executeFeatureProcess('p5:getSelectedFeatures');
+
 
 
 function p5Utils__arrayRemove(array, element) {
 	return array.filter(e => e !== element);
 }
 
+function fixSelectAllInSelectedState(state) {
+	var len = state.listPrimaryKeys.length;
+	var selLen = state.selected.length;
+	var isAllSelected = ( len > 0 && selLen >= len ); // selected array may contain 'select-all'
+	for (var i = 0, pk = ''; i < len; i++) {
+		pk = state.listPrimaryKeys[i];
+		if (-1 === state.selected.indexOf(pk)) {
+			isAllSelected = false;
+			break;
+		}
+	}
+	// DBG && console.log('fixSelectAllInSelectedState', { isAllSelected, state });
+	var isSelectedSelectAll = ( -1 !== state.selected.indexOf('select-all') );
+	if (isAllSelected && !isSelectedSelectAll) return Object.assign(state, {
+		selected: ['select-all'].concat(state.selected)
+	});
+	if (!isAllSelected && isSelectedSelectAll) return Object.assign(state, {
+		selected: state.selected.filter(function (pk) {
+			return ( 'select-all' !== pk );
+		})
+	});
+	return state;
+}
 function selectedStore(state, action) { // TODO: fetch from ajax on init, and on change rows in table
-	var prevState = state || { isLoading: false, selected: [], loading: [], listPrimaryKeys: [] };
+	var prevState = state || { isLoading: false, selected: [], loading: [], listPrimaryKeys: [], totalSelected: 0 };
 	DBG && console.log('DBG::selectedStore', { prevState, action });
 	switch (action.type) {
-		case 'SET_CHECKED': return Object.assign(prevState, {
+		case 'SET_CHECKED': return fixSelectAllInSelectedState(Object.assign(prevState, {
 			loading: p5Utils__arrayRemove(prevState.loading, action.primaryKey),
-			selected: prevState.selected.concat(action.primaryKey)
-		});
-		case 'SET_UNCHECKED': return Object.assign(prevState, {
+			selected: prevState.selected.concat(action.primaryKey),
+			totalSelected: action.totalSelected,
+		}));
+		case 'SET_UNCHECKED': return fixSelectAllInSelectedState(Object.assign(prevState, {
 			loading: p5Utils__arrayRemove(prevState.loading, action.primaryKey),
-			selected: p5Utils__arrayRemove(prevState.selected, action.primaryKey)
-		});
-		case 'SET_LIST_CHECKED': return Object.assign(prevState, {
+			selected: p5Utils__arrayRemove(prevState.selected, action.primaryKey),
+			totalSelected: action.totalSelected,
+		}));
+		case 'SET_LIST_CHECKED': return fixSelectAllInSelectedState(Object.assign(prevState, {
 			loading: [], // TODO: p5Utils__arrayExcept(prevState.loading, action.listPrimaryKeys),
 			selected: [].concat(action.listPrimaryKeys), // TODO: p5Utils__arrayUniqueUnion(prevState.selected, action.listPrimaryKeys)
-		});
-		case 'SET_LIST_UNCHECKED': return Object.assign(prevState, {
+			totalSelected: action.totalSelected,
+		}));
+		case 'SET_LIST_UNCHECKED': return fixSelectAllInSelectedState(Object.assign(prevState, {
 			loading: [], // TODO: p5Utils__arrayExcept(prevState.loading, action.listPrimaryKeys),
 			selected: [], // TODO: p5Utils__arrayUniqueUnion(prevState.selected, action.listPrimaryKeys)
-		});
+			totalSelected: action.totalSelected,
+		}));
 		case 'SET_LOADING': return Object.assign(prevState, {
 			loading: prevState.loading.concat(action.primaryKey)
 		});
 		case 'SET_LIST_LOADING': return Object.assign(prevState, {
 			loading: [].concat(action.listPrimaryKeys)
 		});
-		case 'SET_PRIMARY_KEYS': return Object.assign(prevState, {
-			// TODO: remove from loading and selected pks which are not in action.listPrimaryKeys, exclude 'select-all'
+		case 'SET_PRIMARY_KEYS': return fixSelectAllInSelectedState(Object.assign(prevState, {
 			listPrimaryKeys: [].concat(action.listPrimaryKeys),
-		});
+			loading: [].concat(action.listPrimaryKeys)
+		}));
+		// case 'SET_ERROR_MSG': return Object.assign(prevState, {
+		// 	errorMsg: action.errorMsg
+		// });
 		default: return prevState;
 	}
 }
 function selectedActions() {
-	function setChecked(primaryKey) {
-		return { type: 'SET_CHECKED', primaryKey: primaryKey }
+	function setChecked(primaryKey, totalSelected) {
+		return { type: 'SET_CHECKED', primaryKey: primaryKey, totalSelected: totalSelected }
 	}
-	function setUnchecked(primaryKey) {
-		return { type: 'SET_UNCHECKED', primaryKey: primaryKey }
+	function setUnchecked(primaryKey, totalSelected) {
+		return { type: 'SET_UNCHECKED', primaryKey: primaryKey, totalSelected: totalSelected }
 	}
 	function setLoading(primaryKey) {
 		return { type: 'SET_LOADING', primaryKey: primaryKey }
 	}
-	function setListChecked(listPrimaryKeys) {
-		return { type: 'SET_LIST_CHECKED', listPrimaryKeys: listPrimaryKeys }
+	function setListChecked(listPrimaryKeys, totalSelected) {
+		return { type: 'SET_LIST_CHECKED', listPrimaryKeys: listPrimaryKeys, totalSelected: totalSelected }
 	}
-	function setListUnchecked(listPrimaryKeys) {
-		return { type: 'SET_LIST_UNCHECKED', listPrimaryKeys: listPrimaryKeys }
+	function setListUnchecked(listPrimaryKeys, totalSelected) {
+		return { type: 'SET_LIST_UNCHECKED', listPrimaryKeys: listPrimaryKeys, totalSelected: totalSelected }
 	}
 	function setListLoading(listPrimaryKeys) {
 		return { type: 'SET_LIST_LOADING', listPrimaryKeys: listPrimaryKeys }
 	}
-	function toggle(primaryKey, checked) {
-		if ('select-all' === primaryKey) return toggleAll(checked);
+	function setErrorMsg(errorMsg) {
+		return { type: 'SET_ERROR_MSG', errorMsg: errorMsg }
+	}
+	function toggle(namespace, primaryKey, checked) {
+		if ('select-all' === primaryKey) return toggleAll(namespace, checked);
 
 		return function(dispatch, getState) {
 			// var state = getState();
 			dispatch(setLoading(primaryKey));
-			p5UI__notifyAjaxCallback({type: 'warning', msg: 'TODO: toggle checkbox for row:' + primaryKey + ' ...'});
-
-			return new Promise(function (resolve, reject) {
-				setTimeout(function () {
-					var returnCode = Math.round(Math.random());
-					DBG && console.log('DBG::async toggle action: (pk:'+primaryKey+') fetched resultCode', returnCode);
-					if (!returnCode) {
-						reject("Error");
-						return;
-					}
-					resolve();
-				}, 1000);
-			}).then(function () {
-				p5UI__notifyAjaxCallback({type: 'warning', msg: 'TODO: toggle checkbox for row:' + primaryKey + '; return random success'});
-				DBG && console.log('DBG::async toggle action: (pk:'+primaryKey+') dispatch(SET_CHECKED)');
-				dispatch( checked ? setChecked(primaryKey) : setUnchecked(primaryKey) );
+
+			var reqPromise = checked ? p5WPS__selecFeature(namespace, [ primaryKey ]) : p5WPS__unselectFeature(namespace, [ primaryKey ]);
+			return reqPromise.then(function (response) {
+				DBG && console.log('DBG::async toggle action: (pk:'+primaryKey+') dispatch...');
+				dispatch( checked ? setChecked(primaryKey, response.totalSelected) : setUnchecked(primaryKey, response.totalSelected) );
 			}).catch(function (e) {
-				p5UI__notifyAjaxCallback({type: 'error', msg: 'TODO: toggle checkbox for row:' + primaryKey + '; return random fail - error: ' + e});
-				DBG && console.warn('DBG::async toggle action: (pk:'+primaryKey+') ERROR dispatch(SET_UNCHECKED)');
-				dispatch( checked ? setUnchecked(primaryKey) : setChecked(primaryKey) );
+				p5UI__notifyAjaxCallback({type: 'error', msg: 'Wystąpił błąd: ' + e});
+				DBG && console.warn('DBG::async toggle action: (pk:'+primaryKey+') ERROR', e);
+				// dispatch( checked ? setUnchecked(primaryKey) : setChecked(primaryKey) );
+				// dispatch( setErrorMsg(e) ); // TODO: show error with msg and refresh button
+				dispatch( setListLoading([]) );
 			});
 		}
 	}
-	function setPrimaryKeys(listPrimaryKeys) { // TODO: should set list primaryKeys, change checkboxes to loading and fetch its state, then update gui
+	function setPrimaryKeys(namespace, listPrimaryKeys) { // TODO: should set list primaryKeys, change checkboxes to loading and fetch its state, then update gui
 		DBG && console.log('DBG::setPrimaryKeys action: (pks:['+listPrimaryKeys.join(',')+'])...');
 		return function(dispatch, getState) {
 			dispatch({ type: 'SET_PRIMARY_KEYS', listPrimaryKeys: listPrimaryKeys });
-			dispatch( setListLoading(listPrimaryKeys) );
-			p5UI__notifyAjaxCallback({type: 'warning', msg: 'TODO: update primary keys list:' + listPrimaryKeys.join(',') + '...'});
-
-			return new Promise(function (resolve, reject) {
-				setTimeout(function () {
-					var returnCode = 1;
-					var listSelected = listPrimaryKeys.filter(function (pk) {
-						return Math.round(Math.random());
-					})
-					DBG && console.log('DBG::setPrimaryKeys action: (pks:['+listPrimaryKeys.join(',')+']) fetched', { returnCode, listSelected });
-					if (!returnCode) {
-						reject("Error");
-						return;
-					}
-					resolve(listSelected);
-				}, 1000);
-			}).then(function (listSelected) {
-				p5UI__notifyAjaxCallback({type: 'warning', msg: 'TODO: update primary keys list:' + listPrimaryKeys.join(',') + '; return success'});
+			// dispatch( setListLoading(listPrimaryKeys) );
+			if (!listPrimaryKeys.length) return;
+
+			return p5WPS__getSelectedFeatures(namespace, listPrimaryKeys).then(function (listSelected) {
 				DBG && console.log('DBG::setPrimaryKeys action: (pks:['+listPrimaryKeys.join(',')+']) dispatch success');
-				var fullListPks = ['select-all'].concat(listPrimaryKeys);
-				dispatch( setListUnchecked(fullListPks) );
-				dispatch( setListChecked(listSelected) );
+				dispatch( setListChecked(listSelected.selected, listSelected.totalSelected) );
 			}).catch(function (e) {
-				p5UI__notifyAjaxCallback({type: 'error', msg: 'TODO: update primary keys list:' + listPrimaryKeys.join(',') + '; return fail - error: ' + e});
+				p5UI__notifyAjaxCallback({type: 'error', msg: 'Wystąpił błąd: ' + e});
 				DBG && console.warn('DBG::setPrimaryKeys action: (pks:['+listPrimaryKeys.join(',')+']) ERROR dispatch error');
-				var fullListPks = ['select-all'].concat(listPrimaryKeys);
-				dispatch( setListUnchecked(fullListPks) );
+				// var fullListPks = ['select-all'].concat(listPrimaryKeys);
+				// dispatch( setListUnchecked(fullListPks) );
+				// dispatch( setErrorMsg(e) ); // TODO: show error with msg and refresh button
+				dispatch( setListLoading([]) );
 			});
 		}
 	}
-	function toggleAll(checked) {
+	function toggleAll(namespace, checked) {
 		var primaryKey = 'select-all';
 		return function(dispatch, getState) {
 			// var state = getState();
 			dispatch(setLoading(primaryKey));
 			var state = getState();
 			var listPrimaryKeys = state.listPrimaryKeys;
-			p5UI__notifyAjaxCallback({type: 'warning', msg: 'TODO: toggle all:' + listPrimaryKeys.join(',') + '...'});
-
-			return new Promise(function (resolve, reject) {
-				setTimeout(function () {
-					var returnCode = 1;
-					DBG && console.log('DBG::setPrimaryKeys action: (pks:['+listPrimaryKeys.join(',')+']) fetched resultCode', returnCode);
-					if (!returnCode) {
-						reject("Error");
-						return;
-					}
-					resolve();
-				}, 1000);
-			}).then(function () {
-				p5UI__notifyAjaxCallback({type: 'warning', msg: 'TODO: toggle all:' + listPrimaryKeys.join(',') + '; return success'});
+
+			var reqPromise = checked ? p5WPS__selecFeature(namespace, listPrimaryKeys) : p5WPS__unselectFeature(namespace, listPrimaryKeys);
+			return reqPromise.then(function (response) {
 				DBG && console.log('DBG::setPrimaryKeys action: (pks:['+listPrimaryKeys.join(',')+']) dispatch success');
-				var fullListPks = ['select-all'].concat(listPrimaryKeys)
-				dispatch( checked ? setListChecked(fullListPks) : setListUnchecked(fullListPks) );
+				dispatch( checked ? setListChecked(response.selected, response.totalSelected) : setListUnchecked(response.selected, response.totalSelected) );
 			}).catch(function (e) {
-				p5UI__notifyAjaxCallback({type: 'error', msg: 'TODO: toggle all:' + listPrimaryKeys.join(',') + '; return fail - error: ' + e});
+				p5UI__notifyAjaxCallback({type: 'error', msg: 'Wystąpił błąd: ' + e});
 				DBG && console.warn('DBG::setPrimaryKeys action: (pks:['+listPrimaryKeys.join(',')+']) ERROR dispatch error');
-				var fullListPks = ['select-all'].concat(listPrimaryKeys)
-				dispatch( checked ? setListUnchecked(fullListPks) : setListChecked(fullListPks) );
+				// var fullListPks = ['select-all'].concat(listPrimaryKeys)
+				// dispatch( checked ? setListUnchecked(fullListPks) : setListChecked(fullListPks) );
+				// dispatch( setErrorMsg(e) ); // TODO: show error with msg and refresh button
+				dispatch( setListLoading([]) );
 			});
 		}
 	}
+	function unselectAll(namespace) {
+		// dispatch(setLoading());
+		return function(dispatch, getState) {
+			var reqPromise = p5WPS__unselecAllFeatures(namespace, []);
+			return reqPromise.then(function (response) {
+				DBG && console.log('DBG::setPrimaryKeys action: (pks:['+listPrimaryKeys.join(',')+']) dispatch success');
+				// dispatch( setListChecked(response.selected, response.totalSelected) );
+				dispatch( setListChecked([], 0) );
+			}).catch(function (e) {
+				p5UI__notifyAjaxCallback({type: 'error', msg: 'Wystąpił błąd: ' + e});
+				DBG && console.warn('DBG::setPrimaryKeys action: (pks:['+listPrimaryKeys.join(',')+']) ERROR dispatch error');
+				// var fullListPks = ['select-all'].concat(listPrimaryKeys)
+				// dispatch( checked ? setListUnchecked(fullListPks) : setListChecked(fullListPks) );
+				// dispatch( setErrorMsg(e) ); // TODO: show error with msg and refresh button
+				// dispatch( setListLoading([]) );
+			});
+		};
+	}
 	return {
 		setPrimaryKeys: setPrimaryKeys,
 		setChecked: setChecked,
 		setUnchecked: setUnchecked,
-		toggle: toggle
+		toggle: toggle,
+		unselectAll: unselectAll,
 	}
 }
 
 global['P5UI__TableAjaxRowCheckbox'] = createReactClass({
+	// props.namespace: PropTypes.string.isRequired
 	// props.primaryKey: PropTypes.string.isRequired
 	// props.store: Redux store with state: { isLoading bool, selected array of strings }
 	// props.actions: Redux store actions
@@ -188,29 +282,29 @@ global['P5UI__TableAjaxRowCheckbox'] = createReactClass({
 		return this.getStateFromStore();
 	},
 	componentDidMount: function () {
-		DBG && console.log('DBG::P5UI__TableAjaxRowCheckbox::componentDidMount', { primaryKey: this.props.primaryKey });
+		DBG && console.log('DBG::P5UI__TableAjaxRowCheckbox::componentDidMount (pk:'+this.props.primaryKey+')');
 		this.unsubscribe = this.props.store.subscribe(this.storeUpdated)
 	},
 	componentWillUnmount: function () {
 		this.unsubscribe()
 	},
 	storeUpdated: function () {
-		DBG && console.log('DBG::P5UI__TableAjaxRowCheckbox::storeUpdated', { primaryKey: this.props.primaryKey });
+		DBG && console.log('DBG::P5UI__TableAjaxRowCheckbox::storeUpdated (pk:'+this.props.primaryKey+')');
 		this.setState(this.getStateFromStore())
 	},
 	shouldComponentUpdate: function (nextProps, nextState) {
-		DBG && console.log('DBG::P5UI__TableAjaxRowCheckbox::shouldComponentUpdate', { primaryKey: this.props.primaryKey }, {state: this.state, nextState});
+		DBG && console.log('DBG::P5UI__TableAjaxRowCheckbox::shouldComponentUpdate (pk:'+this.props.primaryKey+')', { state: this.state, nextState});
 		return (
 			this.state.isLoading !== nextState.isLoading
 			|| this.state.checked !== nextState.checked
 		);
 	},
 	handleChange: function (checked) { // handleChange: function (event) {
-		DBG && console.log('DBG::P5UI__TableAjaxRowCheckbox::handleChange', { primaryKey: this.props.primaryKey, checked: checked });
-		this.props.store.dispatch( this.props.actions.toggle( this.props.primaryKey, checked ) )
+		DBG && console.log('DBG::P5UI__TableAjaxRowCheckbox::handleChange (pk:'+this.props.primaryKey+')', { checked: checked });
+		this.props.store.dispatch( this.props.actions.toggle( this.props.namespace, this.props.primaryKey, checked ) )
 	},
 	render: function () {
-		DBG && console.log('DBG::P5UI__TableAjaxRowCheckbox::render', { primaryKey: this.props.primaryKey, checked: this.state.checked, isLoading: this.state.isLoading });
+		DBG && console.log('DBG::P5UI__TableAjaxRowCheckbox::render (pk:'+this.props.primaryKey+')', { props: this.props, state: this.state });
 		return h(p5UI__FieldCheckboxLoading, {
 			size: 20,
 			color: '#999',
@@ -222,6 +316,63 @@ global['P5UI__TableAjaxRowCheckbox'] = createReactClass({
 	}
 });
 
+global['P5UI__TableAjaxSelectedInfo'] = createReactClass({
+	// props.namespace: PropTypes.string.isRequired
+	// props.store: Redux store with state: { isLoading bool, selected array of strings }
+	// props.actions: Redux store actions
+	getStateFromStore: function () {
+		var state = this.props.store.getState();
+		return {
+			// isLoading: state.isLoading || 0, // add ?
+			totalSelected: state.totalSelected || 0,
+		};
+	},
+	getInitialState: function() {
+		return this.getStateFromStore();
+	},
+	componentDidMount: function () {
+		DBG && console.log('DBG::P5UI__TableAjaxSelectedInfo::componentDidMount (ns:'+this.props.namespace+')');
+		this.unsubscribe = this.props.store.subscribe(this.storeUpdated)
+	},
+	componentWillUnmount: function () {
+		this.unsubscribe()
+	},
+	storeUpdated: function () {
+		DBG && console.log('DBG::P5UI__TableAjaxSelectedInfo::storeUpdated (ns:'+this.props.namespace+')');
+		this.setState(this.getStateFromStore())
+	},
+	shouldComponentUpdate: function (nextProps, nextState) {
+		DBG && console.log('DBG::P5UI__TableAjaxSelectedInfo::shouldComponentUpdate (ns:'+this.props.namespace+')', { state: this.state, nextState});
+		return (
+			this.state.totalSelected !== nextState.totalSelected
+			// || this.state.isLoading !== nextState.isLoading
+		);
+	},
+	handleUnselectAll: function (event) {
+		DBG && console.log('DBG::P5UI__TableAjaxSelectedInfo::handleChange (ns:'+this.props.namespace+')', { checked: checked });
+		this.props.store.dispatch( this.props.actions.unselectAll( this.props.namespace ) )
+	},
+	render: function () {
+		DBG && console.log('DBG::P5UI__TableAjaxSelectedInfo::render (ns:'+this.props.namespace+')', { props: this.props, state: this.state });
+		return h('div', { style: {
+			padding: '0 8px'
+		} }, [
+			h('span', {}, "Wybrano " + this.state.totalSelected),
+			(this.state.totalSelected > 0) ? h('i', {
+				onClick: this.handleUnselectAll,
+				className: "glyphicon glyphicon-remove",
+				style: {
+					marginLeft: '4px',
+					cursor: 'pointer',
+					color: 'red',
+					opacity: '0.5'
+				},
+				title: this.props.title || 'Usuń wszystkie zaznaczenia'
+			}) : null,
+		])
+	}
+});
+
 var TableAjax = function() {
 	var priv = {}; //private api
 	var publ = {}; //public api
@@ -271,6 +422,7 @@ var TableAjax = function() {
 	};
 
 	var _uiNodeCont; // container holding table
+	var _uiNodeSelectedInfo;
 	var _uiNode$Table; // the table node
 	var _head; // table header
 	var _headSort; // table header sorting row
@@ -351,6 +503,16 @@ var TableAjax = function() {
 		if (priv.options.checkboxes) {
 			priv.options.selectedStore = createStoreWithThunkMiddleware(selectedStore)
 			priv.options.selectedActions = selectedActions()
+			if (_uiNodeSelectedInfo) { // must run after priv.initialRender(); - _uiNodeSelectedInfo must exists
+				ReactDOM.render(
+					h(P5UI__TableAjaxSelectedInfo, {
+						namespace: priv.options.namespace,
+						store: priv.options.selectedStore,
+						actions: priv.options.selectedActions
+					}),
+					_uiNodeSelectedInfo
+				);
+			}
 		}
 	};
 
@@ -429,6 +591,8 @@ var TableAjax = function() {
 		_popoverCell = $('<div class="popoverCell" style="display:none"></div>').insertAfter(_foot);
 		$('<div class="tblAjax__inlineEditBox"></div>').insertAfter(_foot);
 		$('<div class="btn-toolbar tblAjax__head__specialFilter"></div>').insertBefore(_uiNodeCont);
+		_uiNodeSelectedInfo = document.createElement('div')
+		_uiNodeCont.parentNode.insertBefore(_uiNodeSelectedInfo, _uiNodeCont)
 
 		priv.renderInlineEditBox();// .tblAjax__inlineEditBox
 
@@ -867,6 +1031,7 @@ var TableAjax = function() {
 			cellNode = $('<td class="stickyCol stickyCol2" style="padding:3px 5px"></td>').appendTo(rowNode);
 			ReactDOM.render(
 				h(P5UI__TableAjaxRowCheckbox, {
+					namespace: priv.options.namespace,
 					primaryKey: rowPK,
 					store: priv.options.selectedStore,
 					actions: priv.options.selectedActions
@@ -1218,6 +1383,7 @@ var TableAjax = function() {
 			var headCell = $('<th class="stickyCol stickyCol2" style="padding:3px 5px"></th>').appendTo(node);
 			ReactDOM.render(
 				h(P5UI__TableAjaxRowCheckbox, {
+					namespace: priv.options.namespace,
 					primaryKey: 'select-all',
 					store: priv.options.selectedStore,
 					actions: priv.options.selectedActions
@@ -2063,7 +2229,7 @@ var TableAjax = function() {
 		;
 		jQuery(_uiNodeCont).trigger('TableAjax:updateRows', { primaryKeys: listPrimaryKeys });
 		if (priv.options.checkboxes) {
-			priv.options.selectedStore.dispatch( priv.options.selectedActions.setPrimaryKeys(listPrimaryKeys) );
+			priv.options.selectedStore.dispatch( priv.options.selectedActions.setPrimaryKeys(priv.options.namespace, listPrimaryKeys) );
 		}
 		var data = $.extend(true, {}, pData);
 		data.cols = _state.cols;// always use old cols - change cols mved to priv.setStateCols