asab_webui_shell 25.1.8 → 25.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -124,6 +124,10 @@ var Application = /*#__PURE__*/function (_Component) {
124
124
  _this.ReduxService.addReducer("sidebar", _reducer2["default"]);
125
125
  _this.ReduxService.addReducer("navigation", _reducer4["default"]);
126
126
  _this.ReduxService.addReducer("router", _reducer5["default"]);
127
+
128
+ // The application can provide a list of keys that should be automatically parsed as BigInt, when received from the server in Axios call
129
+ // This is important to preserve 64bit numbers from the server such as IP addresses, timestamps etc.
130
+ _this.JSONParseBigInt = new Set(props === null || props === void 0 ? void 0 : props.bigint);
127
131
  _this._handleKeyUp = _this._handleKeyUp.bind(_this);
128
132
  _this.state = {
129
133
  networking: 0,
@@ -337,6 +341,10 @@ var Application = /*#__PURE__*/function (_Component) {
337
341
  return undefined;
338
342
  }
339
343
  var axios = _axios["default"].create(_objectSpread(_objectSpread({}, props), {}, {
344
+ transformResponse: function transformResponse(res) {
345
+ return res;
346
+ },
347
+ // Disable implicit JSON parsing, we explicitly parse JSON in the interceptor ( https://stackoverflow.com/questions/41013082/disable-json-parsing-in-axios )
340
348
  baseURL: service_url
341
349
  }));
342
350
 
@@ -370,9 +378,49 @@ var Application = /*#__PURE__*/function (_Component) {
370
378
  return Promise.reject(error);
371
379
  });
372
380
 
373
- // We want to remove networking indicator when we recieve the response
381
+ // We want to remove the networking indicator when we receive the response
374
382
  axios.interceptors.response.use(function (response) {
383
+ var _response$headers$con;
384
+ // Call the `popNetworkingIndicator` method to remove the networking indicator (e.g., loading spinner)
375
385
  that.popNetworkingIndicator();
386
+
387
+ /*
388
+ Custom flag to control JSON parsing for specific requests.
389
+ If 'skipJsonParsing' is set to true in the request config,
390
+ we return the raw response and skip automatic JSON parsing in the interceptor.
391
+ This is useful when we need to get the response as plain text (e.g., for retrieving raw content).
392
+ */
393
+ if (response.config.skipJsonParsing) {
394
+ return response;
395
+ }
396
+
397
+ // Check if the response content type is 'application/json' or starts with 'application/json'
398
+ if (response !== null && response !== void 0 && (_response$headers$con = response.headers['content-type']) !== null && _response$headers$con !== void 0 && _response$headers$con.startsWith('application/json')) {
399
+ // If the response is JSON, then parse it with respect to BigInt
400
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse
401
+ try {
402
+ // Parse the response data using a custom reviver function
403
+ response.data = JSON.parse(response.data,
404
+ // Custom reviver function to handle BigInt values
405
+ function (key, value, context) {
406
+ // If there is no data in the JSONParseBigInt, return the value immediately
407
+ if (that.JSONParseBigInt.size === 0) {
408
+ return value;
409
+ }
410
+ // Check if the key is in the `JSONParseBigInt` set and the value is a number
411
+ if (that.JSONParseBigInt.has(key) && typeof value === 'number') {
412
+ // Convert the number to a BigInt
413
+ return BigInt(context.source);
414
+ }
415
+ // Return the original value if no conversion is needed
416
+ return value;
417
+ });
418
+ } catch (e) {
419
+ // Log any errors that occur during JSON parsing
420
+ console.error("Error in axios.interceptors.response", e);
421
+ }
422
+ }
423
+ // If the request was satisfied (application/json and presence of BigInt) return the modified object. If not, we return unchanged object
376
424
  return response;
377
425
  }, function (error) {
378
426
  that.popNetworkingIndicator();
@@ -27,6 +27,14 @@ function InvitationScreen(props) {
27
27
  _useState4 = (0, _slicedToArray2["default"])(_useState3, 2),
28
28
  isInvitationSuccessful = _useState4[0],
29
29
  setIsInvitationSuccessful = _useState4[1];
30
+ var _useState5 = (0, _react.useState)(undefined),
31
+ _useState6 = (0, _slicedToArray2["default"])(_useState5, 2),
32
+ registrationUrl = _useState6[0],
33
+ setRegistrationUrl = _useState6[1];
34
+ var _useState7 = (0, _react.useState)(undefined),
35
+ _useState8 = (0, _slicedToArray2["default"])(_useState7, 2),
36
+ urlCopied = _useState8[0],
37
+ setUrlCopied = _useState8[1];
30
38
  var tenant = (0, _reactRedux.useSelector)(function (state) {
31
39
  var _state$tenant;
32
40
  return (_state$tenant = state.tenant) === null || _state$tenant === void 0 ? void 0 : _state$tenant.current;
@@ -36,6 +44,25 @@ function InvitationScreen(props) {
36
44
  var navigate = (0, _reactRouterDom.useNavigate)();
37
45
  var SeaCatAuthAPI = props.app.axiosCreate('seacat-auth');
38
46
 
47
+ // Copy registration URL if there is any
48
+ var copyRegistrationUrl = function copyRegistrationUrl() {
49
+ if (!registrationUrl) {
50
+ console.error('No registration URL to copy.');
51
+ return;
52
+ }
53
+ navigator.clipboard.writeText(registrationUrl).then(function () {
54
+ setUrlCopied(true);
55
+ var timeoutId = setTimeout(function () {
56
+ return setUrlCopied(false);
57
+ }, 3000);
58
+ return function () {
59
+ clearTimeout(timeoutId);
60
+ };
61
+ })["catch"](function (error) {
62
+ console.error('Failed to copy registration URL: ', error);
63
+ });
64
+ };
65
+
39
66
  // Validate email input
40
67
  var emailValidation = function emailValidation(e) {
41
68
  e.preventDefault();
@@ -45,7 +72,7 @@ function InvitationScreen(props) {
45
72
  // Send invitation to the specified email
46
73
  var sendInvitation = /*#__PURE__*/function () {
47
74
  var _ref = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee(e) {
48
- var body, _response$data, response;
75
+ var body, _response$data, _response$data2, response, _response$data3, _e$response, _e$response2;
49
76
  return _regenerator["default"].wrap(function _callee$(_context) {
50
77
  while (1) switch (_context.prev = _context.next) {
51
78
  case 0:
@@ -66,29 +93,57 @@ function InvitationScreen(props) {
66
93
  case 8:
67
94
  setIsInvitationSuccessful(true);
68
95
  setEmailValue('');
69
- _context.next = 15;
96
+ if (response !== null && response !== void 0 && (_response$data2 = response.data) !== null && _response$data2 !== void 0 && _response$data2.registration_url) {
97
+ setRegistrationUrl(response === null || response === void 0 || (_response$data3 = response.data) === null || _response$data3 === void 0 ? void 0 : _response$data3.registration_url);
98
+ } else {
99
+ setRegistrationUrl(undefined);
100
+ }
101
+ _context.next = 17;
70
102
  break;
71
- case 12:
72
- _context.prev = 12;
103
+ case 13:
104
+ _context.prev = 13;
73
105
  _context.t0 = _context["catch"](2);
74
106
  setIsInvitationSuccessful(false);
75
- case 15:
107
+ if (_context.t0 !== null && _context.t0 !== void 0 && (_e$response = _context.t0.response) !== null && _e$response !== void 0 && (_e$response = _e$response.data) !== null && _e$response !== void 0 && _e$response.registration_url) {
108
+ setRegistrationUrl(_context.t0 === null || _context.t0 === void 0 || (_e$response2 = _context.t0.response) === null || _e$response2 === void 0 || (_e$response2 = _e$response2.data) === null || _e$response2 === void 0 ? void 0 : _e$response2.registration_url);
109
+ } else {
110
+ setRegistrationUrl(undefined);
111
+ }
112
+ case 17:
76
113
  case "end":
77
114
  return _context.stop();
78
115
  }
79
- }, _callee, null, [[2, 12]]);
116
+ }, _callee, null, [[2, 13]]);
80
117
  }));
81
118
  return function sendInvitation(_x) {
82
119
  return _ref.apply(this, arguments);
83
120
  };
84
121
  }();
85
122
 
123
+ // Component that displays the registration URL with a copy button
124
+ var CopyableRegistrationUrl = function CopyableRegistrationUrl(_ref2) {
125
+ var registrationUrl = _ref2.registrationUrl;
126
+ return /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, /*#__PURE__*/_react["default"].createElement(_reactstrap.FormText, null, t('InvitationScreen|If you want to invite the user manually, message them the registration URL below:')), /*#__PURE__*/_react["default"].createElement(_reactstrap.InputGroup, null, /*#__PURE__*/_react["default"].createElement(_reactstrap.Input, {
127
+ readOnly: true,
128
+ value: registrationUrl
129
+ }), /*#__PURE__*/_react["default"].createElement(_reactstrap.Button, {
130
+ outline: true,
131
+ color: "primary",
132
+ className: "text-nowrap",
133
+ onClick: copyRegistrationUrl
134
+ }, /*#__PURE__*/_react["default"].createElement("i", {
135
+ className: urlCopied ? 'bi bi-clipboard-check pe-2' : 'bi bi-clipboard pe-2',
136
+ title: t('InvitationScreen|Copy to clipboard')
137
+ }), urlCopied ? t('InvitationScreen|Copied!') : t('InvitationScreen|Copy URL'))));
138
+ };
139
+
86
140
  // Display for successful invitation
87
141
  var SuccessfulInvitationCardBody = function SuccessfulInvitationCardBody() {
88
- return /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, /*#__PURE__*/_react["default"].createElement("h6", null, t('InvitationScreen|Invitation has been sent successfully')), /*#__PURE__*/_react["default"].createElement("div", {
142
+ return /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, /*#__PURE__*/_react["default"].createElement("h6", null, t('InvitationScreen|Invitation was sent successfully')), registrationUrl && /*#__PURE__*/_react["default"].createElement(CopyableRegistrationUrl, {
143
+ registrationUrl: registrationUrl
144
+ }), /*#__PURE__*/_react["default"].createElement("div", {
89
145
  className: "mt-2"
90
146
  }, /*#__PURE__*/_react["default"].createElement(_reactstrap.Button, {
91
- block: true,
92
147
  onClick: function onClick() {
93
148
  return navigate('/auth/credentials');
94
149
  },
@@ -99,10 +154,11 @@ function InvitationScreen(props) {
99
154
 
100
155
  // Display for unsuccessful invitation
101
156
  var UnsuccessfulInvitationCardBody = function UnsuccessfulInvitationCardBody() {
102
- return /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, /*#__PURE__*/_react["default"].createElement("h6", null, t('InvitationScreen|Invitation has not been sent')), /*#__PURE__*/_react["default"].createElement(_reactstrap.FormText, null, t('InvitationScreen|The user could not be invited')), /*#__PURE__*/_react["default"].createElement("div", {
157
+ return /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, /*#__PURE__*/_react["default"].createElement("h6", null, t('InvitationScreen|Invitation was not sent')), registrationUrl ? /*#__PURE__*/_react["default"].createElement(CopyableRegistrationUrl, {
158
+ registrationUrl: registrationUrl
159
+ }) : /*#__PURE__*/_react["default"].createElement(_reactstrap.FormText, null, t('InvitationScreen|The user could not be invited')), /*#__PURE__*/_react["default"].createElement("div", {
103
160
  className: "mt-2"
104
161
  }, /*#__PURE__*/_react["default"].createElement(_reactstrap.Button, {
105
- block: true,
106
162
  onClick: function onClick() {
107
163
  return setIsInvitationSuccessful(undefined);
108
164
  },
@@ -132,7 +188,7 @@ function InvitationScreen(props) {
132
188
  className: "flex-fill"
133
189
  }, /*#__PURE__*/_react["default"].createElement("h3", null, /*#__PURE__*/_react["default"].createElement("i", {
134
190
  className: "bi bi-person-plus pe-2"
135
- }), t('InvitationScreen|Invite other users')))), /*#__PURE__*/_react["default"].createElement(_reactstrap.CardBody, null, /*#__PURE__*/_react["default"].createElement(_reactstrap.Label, null, t('InvitationScreen|Enter the email to invite a user')), /*#__PURE__*/_react["default"].createElement(_reactstrap.InputGroup, null, /*#__PURE__*/_react["default"].createElement(_reactstrap.InputGroupText, null, /*#__PURE__*/_react["default"].createElement("i", {
191
+ }), t('InvitationScreen|Invite other users')))), /*#__PURE__*/_react["default"].createElement(_reactstrap.CardBody, null, /*#__PURE__*/_react["default"].createElement(_reactstrap.Label, null, t('InvitationScreen|Enter the user\'s email address')), /*#__PURE__*/_react["default"].createElement(_reactstrap.InputGroup, null, /*#__PURE__*/_react["default"].createElement(_reactstrap.InputGroupText, null, /*#__PURE__*/_react["default"].createElement("i", {
136
192
  className: "bi bi-envelope-at"
137
193
  })), /*#__PURE__*/_react["default"].createElement(_reactstrap.Input, {
138
194
  name: "email",
@@ -157,5 +213,5 @@ function InvitationScreen(props) {
157
213
  }, "\xA0"), /*#__PURE__*/_react["default"].createElement(_reactstrap.Button, {
158
214
  color: "primary",
159
215
  disabled: emailValue === '' // Disable button if input is empty
160
- }, t('InvitationScreen|Send'))))))));
216
+ }, t('InvitationScreen|Invite'))))))));
161
217
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "asab_webui_shell",
3
- "version": "25.1.8",
3
+ "version": "25.1.10",
4
4
  "license": "BSD-3-Clause",
5
5
  "description": "TeskaLabs ASAB WebUI Shell Application",
6
6
  "contributors": [