WindykacjaUpdateStatus.php.view.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. var DBG = DBG || 0;
  2. var DBG1 = true;
  3. var EXECUTE_NEXT_ACTION_LIMIT = 1000;
  4. var EXECUTE_NEXT_ACTION_INTERVAL = 100;
  5. if (!HTML_ID) throw "Missing HTML_ID";
  6. if (!TOTAL) throw "Missing TOTAL";
  7. if (!EXECUTE_SINGLE_TASK_URL) throw "Missing EXECUTE_SINGLE_TASK_URL";
  8. if (!global.p5VendorJs) throw "Missing vendor.js";
  9. var ALLOW_FORCE_UPDATE = ALLOW_FORCE_UPDATE || false;
  10. var FORCE_UPDATE_POST_DATA = FORCE_UPDATE_POST_DATA || [];
  11. var createReactClass = global.p5VendorJs.createReactClass;
  12. var h = global.p5VendorJs.React.createElement;
  13. var ReactDOM = global.p5VendorJs.ReactDOM;
  14. // var AsyncTypeahead = window.p5VendorJs.AsyncTypeahead;
  15. // responseBody:
  16. // - percentDone
  17. // - totalToUpdate
  18. // - total
  19. // - totalDone
  20. var sendRequest = (function () { // uses: EXECUTE_SINGLE_TASK_URL
  21. var _versionSent = 1;
  22. var _versionReceived = 0;
  23. return function () {// return function (limit, page, sorted, filtered) {
  24. // var filterTableProps = {
  25. // limit: limit || REQUEST_DATA_LIMIT,
  26. // page: page || 0,
  27. // sorted: sorted || [],
  28. // filtered: filtered || [],
  29. // };
  30. _versionSent++;
  31. var this_version = _versionSent;
  32. return global.fetch(EXECUTE_SINGLE_TASK_URL, {
  33. header: { 'contentType': 'applications/json' },
  34. credentials: 'same-origin',
  35. method: 'GET',
  36. // body: JSON.stringify(filterTableProps), // POST
  37. }).then(function (result) {
  38. if (this_version < _versionReceived) throw "Skipped response: already received newer version.";
  39. return result.json()
  40. }).then(function (data) {
  41. DBG && console.log("DBG:EXECUTE_SINGLE_TASK_URL:Data", { data })
  42. _versionReceived = this_version;
  43. // DBG && console.log("DBG:sendRequest:afterReceived", { _versionSent, _versionReceived, _lastPropsJson, this_version, this_lastPropsJson });
  44. return data.body; // body: { rows: [], total: int }
  45. })
  46. }
  47. })();
  48. var P5UI__WindykacjaStatusUpdate = createReactClass({
  49. getInitialState: function () {
  50. return {
  51. errorMsg: '',
  52. loading: false,
  53. version: 0,
  54. responseBody: null,
  55. execLimit: EXECUTE_NEXT_ACTION_LIMIT,
  56. execCounter: 0,
  57. execAgain: true,
  58. }
  59. },
  60. componentDidMount: function () {
  61. this.runUpdateStatus();
  62. },
  63. runAgainIfNotDone: function () {
  64. if (!this.state.execAgain) return;
  65. if (this.state.responseBody && 100 === this.state.responseBody.percentDone && 0 === this.state.responseBody.totalToUpdate) {
  66. return;
  67. }
  68. if (this.state.execCounter > this.state.execLimit) {
  69. this.setState({
  70. errorMsg: "Osiągnięto limit wywołań funkcji",
  71. })
  72. return;
  73. }
  74. var _runUpdateStatus = this.runUpdateStatus.bind(this);
  75. setTimeout(_runUpdateStatus, EXECUTE_NEXT_ACTION_INTERVAL)
  76. },
  77. runUpdateStatus: function () {
  78. DBG && console.log('DBG:runUpdateStatus', {});
  79. this.setState({ loading: true });
  80. var _runAgainIfNotDone = this.runAgainIfNotDone.bind(this);
  81. var _setState = this.setState.bind(this);
  82. var _version = this.state.version + 1; // TODO: handle race condition
  83. sendRequest().then(responseBody => {
  84. _setState({
  85. responseBody: responseBody,
  86. version: _version,
  87. loading: false,
  88. execCounter: this.state.execCounter + 1,
  89. }, _runAgainIfNotDone);
  90. }).catch(function (e) {
  91. if ("Skipped request" === ('' + e).substr(0, "Skipped request".length)) {
  92. console.log("DBG:sendRequest:Skipped", e)
  93. _setState({
  94. loading: false,
  95. }, );
  96. return;
  97. }
  98. console.log("Error:sendRequest", e)
  99. _setState({
  100. loading: false,
  101. errorMsg: '' + e,
  102. });
  103. });
  104. },
  105. handleClickRestart: function (event) {
  106. event.preventDefault()
  107. location.reload()
  108. },
  109. handleClickForceRestart: function (event) {
  110. event.preventDefault()
  111. },
  112. handleClickPause: function (event) {
  113. event.preventDefault()
  114. this.setState({ execAgain: false })
  115. },
  116. handleClickPlay: function (event) {
  117. event.preventDefault()
  118. this.setState({ execAgain: true })
  119. this.runUpdateStatus();
  120. },
  121. render: function () {
  122. DBG && console.log('DBG:render', { state: this.state });
  123. var isDone = (this.state.responseBody && 100 === this.state.responseBody.percentDone && 0 === this.state.responseBody.totalToUpdate);
  124. var percent = (this.state.responseBody) ? Math.round(this.state.responseBody.percentDone, 2) : 0;
  125. var panelClass = (percent < 100) ? "warning" : "success";
  126. if (this.state.errorMsg) panelClass = "danger";
  127. var iconPause = h('i', { className: "glyphicon glyphicon-pause" });
  128. var iconPlay = h('i', { className: "glyphicon glyphicon-play" });
  129. return h('div', { p5_node_id: 'p5-windykacja-status-update-widget' }, [
  130. h('div', { className: "panel panel-" + panelClass }, [
  131. h('div', { className: "panel-heading" }, [
  132. ]),
  133. h('div', { className: "panel-body", style: { paddingBottom: "0" } }, [
  134. h('p', {}, [
  135. "Aktaulizacja statusu " + this.props.total + " rekordów ",
  136. " ",
  137. (!isDone)
  138. ? (
  139. (this.state.execAgain)
  140. ? h('button', { className: "btn btn-xs btn-default", onClick: this.handleClickPause }, [ iconPause, "Zatrzymaj" ])
  141. : h('button', { className: "btn btn-xs btn-default", onClick: this.handleClickPlay }, [ iconPlay, "Uruchom ponownie" ])
  142. )
  143. : '',
  144. ]),
  145. h('div', { className: "progress" }, [
  146. h('div', { className: "progress-bar",
  147. role: "progressbar",
  148. ariaValuenow: percent,
  149. ariaValuemin: "0", ariaValuemax: "100",
  150. style: { width: "" + percent + "%" },
  151. }, [
  152. "" + percent + "%"
  153. ]),
  154. ]),
  155. (this.state.responseBody)
  156. ? h('p', {}, "Wykonano " + this.state.responseBody.totalDone + " z " + this.state.responseBody.total)
  157. : '',
  158. (this.state.errorMsg) ? h('div', { className: "alert alert-danger" }, [
  159. this.state.errorMsg,
  160. " ",
  161. h('button', { className: "btn btn-primary", onClick: this.handleClickRestart }, "Uruchom ponownie")
  162. ]) : "",
  163. (isDone && !this.state.errorMsg && this.props.allowForceUpdate)
  164. ? h('div', { style: { float: "right" } }, [
  165. h(P5UI__PostButton, { data: this.props.forceUpdatePostData, className: "btn btn-xs btn-warning", label: "Wymuś ponowne uruchomienie" })
  166. ])
  167. : '',
  168. ]),
  169. ]),
  170. ]);
  171. }
  172. });
  173. var P5UI__PostButton = function (props) { // @props: { label: string, data: { key: value, ... } }
  174. return h('form', { 'style' : { display : "inline" }, method: "POST" }, [
  175. h('div', {}, Object.keys(props.data).map(function (key) {
  176. return h('input', { type: "hidden", name: key, value: props.data[key] });
  177. })),
  178. h('button', { className: props.className || "btn btn-primary", type: "submit" }, props.label)
  179. ]);
  180. }
  181. ReactDOM.render(
  182. h(P5UI__WindykacjaStatusUpdate, {
  183. total: TOTAL,
  184. allowForceUpdate: ALLOW_FORCE_UPDATE,
  185. forceUpdatePostData: FORCE_UPDATE_POST_DATA,
  186. }),
  187. document.getElementById(HTML_ID)
  188. )