iatoolkit 0.59.1__py3-none-any.whl → 0.67.0__py3-none-any.whl

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.

Potentially problematic release.


This version of iatoolkit might be problematic. Click here for more details.

Files changed (93) hide show
  1. iatoolkit/__init__.py +2 -0
  2. iatoolkit/base_company.py +2 -19
  3. iatoolkit/common/routes.py +28 -25
  4. iatoolkit/common/session_manager.py +2 -0
  5. iatoolkit/common/util.py +17 -6
  6. iatoolkit/company_registry.py +1 -2
  7. iatoolkit/iatoolkit.py +54 -20
  8. iatoolkit/locales/en.yaml +167 -0
  9. iatoolkit/locales/es.yaml +163 -0
  10. iatoolkit/repositories/database_manager.py +3 -3
  11. iatoolkit/repositories/document_repo.py +1 -1
  12. iatoolkit/repositories/models.py +3 -4
  13. iatoolkit/repositories/profile_repo.py +0 -4
  14. iatoolkit/services/auth_service.py +44 -32
  15. iatoolkit/services/branding_service.py +35 -27
  16. iatoolkit/services/configuration_service.py +140 -0
  17. iatoolkit/services/dispatcher_service.py +20 -18
  18. iatoolkit/services/document_service.py +5 -2
  19. iatoolkit/services/excel_service.py +15 -11
  20. iatoolkit/services/file_processor_service.py +4 -12
  21. iatoolkit/services/history_service.py +8 -7
  22. iatoolkit/services/i18n_service.py +104 -0
  23. iatoolkit/services/jwt_service.py +7 -9
  24. iatoolkit/services/language_service.py +79 -0
  25. iatoolkit/services/load_documents_service.py +4 -4
  26. iatoolkit/services/mail_service.py +9 -4
  27. iatoolkit/services/onboarding_service.py +10 -4
  28. iatoolkit/services/profile_service.py +59 -38
  29. iatoolkit/services/prompt_manager_service.py +20 -16
  30. iatoolkit/services/query_service.py +15 -14
  31. iatoolkit/services/sql_service.py +6 -2
  32. iatoolkit/services/user_feedback_service.py +70 -29
  33. iatoolkit/static/js/chat_feedback_button.js +80 -0
  34. iatoolkit/static/js/chat_help_content.js +124 -0
  35. iatoolkit/static/js/chat_history_button.js +110 -0
  36. iatoolkit/static/js/chat_logout_button.js +36 -0
  37. iatoolkit/static/js/chat_main.js +32 -184
  38. iatoolkit/static/js/{chat_onboarding.js → chat_onboarding_button.js} +0 -1
  39. iatoolkit/static/js/chat_prompt_manager.js +94 -0
  40. iatoolkit/static/js/chat_reload_button.js +35 -0
  41. iatoolkit/static/styles/chat_iatoolkit.css +251 -205
  42. iatoolkit/static/styles/chat_modal.css +63 -95
  43. iatoolkit/static/styles/chat_public.css +107 -0
  44. iatoolkit/static/styles/landing_page.css +121 -167
  45. iatoolkit/templates/_company_header.html +20 -0
  46. iatoolkit/templates/_login_widget.html +10 -10
  47. iatoolkit/templates/base.html +36 -19
  48. iatoolkit/templates/change_password.html +24 -22
  49. iatoolkit/templates/chat.html +121 -93
  50. iatoolkit/templates/chat_modals.html +113 -74
  51. iatoolkit/templates/error.html +44 -8
  52. iatoolkit/templates/forgot_password.html +17 -15
  53. iatoolkit/templates/index.html +66 -81
  54. iatoolkit/templates/login_simulation.html +16 -5
  55. iatoolkit/templates/onboarding_shell.html +1 -2
  56. iatoolkit/templates/signup.html +22 -20
  57. iatoolkit/views/base_login_view.py +12 -1
  58. iatoolkit/views/change_password_view.py +50 -33
  59. iatoolkit/views/external_login_view.py +5 -11
  60. iatoolkit/views/file_store_api_view.py +7 -9
  61. iatoolkit/views/forgot_password_view.py +21 -19
  62. iatoolkit/views/help_content_api_view.py +54 -0
  63. iatoolkit/views/history_api_view.py +16 -12
  64. iatoolkit/views/home_view.py +61 -0
  65. iatoolkit/views/index_view.py +5 -34
  66. iatoolkit/views/init_context_api_view.py +16 -13
  67. iatoolkit/views/llmquery_api_view.py +38 -28
  68. iatoolkit/views/login_simulation_view.py +14 -2
  69. iatoolkit/views/login_view.py +48 -33
  70. iatoolkit/views/logout_api_view.py +49 -0
  71. iatoolkit/views/profile_api_view.py +46 -0
  72. iatoolkit/views/prompt_api_view.py +8 -8
  73. iatoolkit/views/signup_view.py +27 -25
  74. iatoolkit/views/{tasks_view.py → tasks_api_view.py} +10 -36
  75. iatoolkit/views/tasks_review_api_view.py +55 -0
  76. iatoolkit/views/user_feedback_api_view.py +21 -32
  77. iatoolkit/views/verify_user_view.py +33 -26
  78. {iatoolkit-0.59.1.dist-info → iatoolkit-0.67.0.dist-info}/METADATA +40 -22
  79. iatoolkit-0.67.0.dist-info/RECORD +120 -0
  80. iatoolkit-0.67.0.dist-info/licenses/LICENSE +21 -0
  81. iatoolkit/static/js/chat_context_reload.js +0 -54
  82. iatoolkit/static/js/chat_feedback.js +0 -115
  83. iatoolkit/static/js/chat_history.js +0 -127
  84. iatoolkit/static/styles/chat_info.css +0 -53
  85. iatoolkit/templates/_branding_styles.html +0 -53
  86. iatoolkit/templates/_navbar.html +0 -9
  87. iatoolkit/templates/header.html +0 -31
  88. iatoolkit/templates/test.html +0 -9
  89. iatoolkit/views/chat_token_request_view.py +0 -98
  90. iatoolkit/views/tasks_review_view.py +0 -83
  91. iatoolkit-0.59.1.dist-info/RECORD +0 -111
  92. {iatoolkit-0.59.1.dist-info → iatoolkit-0.67.0.dist-info}/WHEEL +0 -0
  93. {iatoolkit-0.59.1.dist-info → iatoolkit-0.67.0.dist-info}/top_level.txt +0 -0
@@ -1,47 +1,41 @@
1
1
  // Global variables for request management
2
2
  let isRequestInProgress = false;
3
3
  let abortController = null;
4
-
5
4
  let selectedPrompt = null; // Will hold a lightweight prompt object
6
5
 
7
6
  $(document).ready(function () {
8
- // Gatilla el redeem sin esperar ni manejar respuesta aquí
7
+ // Si viene un Token retornado por login con APY-KEY se gatilla el redeem a una sesion de flask
9
8
  if (window.redeemToken) {
10
- const url = `/api/redeem_token`;
9
+ const url = '/api/redeem_token';
11
10
  // No await: dejamos que callToolkit maneje todo internamente
12
11
  callToolkit(url, {'token': window.redeemToken}, "POST").catch(() => {});
13
12
  }
14
13
 
15
- // --- MAIN EVENT HANDLERS ---
14
+ const layoutContainer = document.querySelector('.chat-layout-container');
15
+ const promptAssistantCollapse = document.getElementById('prompt-assistant-collapse');
16
+
17
+ if (layoutContainer && promptAssistantCollapse) {
18
+ promptAssistantCollapse.addEventListener('show.bs.collapse', function () {
19
+ layoutContainer.classList.add('prompt-assistant-open');
20
+ setTimeout(() => {
21
+ window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
22
+ }, 300);
23
+ });
24
+
25
+ promptAssistantCollapse.addEventListener('hide.bs.collapse', function () {
26
+ layoutContainer.classList.remove('prompt-assistant-open');
27
+ });
28
+ }
29
+
30
+ // --- chat main event hadlers ---
16
31
  $('#send-button').on('click', handleChatMessage);
17
32
  $('#stop-button').on('click', abortCurrentRequest);
18
33
  if (window.sendButtonColor)
19
34
  $('#send-button i').css('color', window.sendButtonColor);
20
35
 
21
- // --- PROMPT ASSISTANT FUNCTIONALITY ---
22
- $('.input-area').on('click', '.dropdown-menu a.dropdown-item', function (event) {
23
- event.preventDefault();
24
- const promptData = $(this).data();
25
-
26
- const promptObject = {
27
- prompt: promptData.promptName,
28
- description: promptData.promptDescription,
29
- custom_fields: typeof promptData.customFields === 'string' ? JSON.parse(promptData.customFields) : promptData.customFields
30
- };
31
-
32
- selectPrompt(promptObject);
33
- });
34
-
35
- // Handles the 'clear' button for the prompt selector
36
- $('#clear-selection-button').on('click', function() {
37
- resetPromptSelection();
38
- updateSendButtonState();
39
- });
40
-
41
- // --- TEXTAREA FUNCTIONALITY ---
42
- const questionTextarea = $('#question');
43
36
 
44
37
  // Handles Enter key press to send a message
38
+ const questionTextarea = $('#question');
45
39
  questionTextarea.on('keypress', function (event) {
46
40
  if (event.key === 'Enter' && !event.shiftKey) {
47
41
  event.preventDefault();
@@ -61,71 +55,8 @@ $(document).ready(function () {
61
55
 
62
56
  // Set the initial disabled state of the send button
63
57
  updateSendButtonState();
64
- });
65
-
66
-
67
- /**
68
- * Handles the selection of a prompt from the dropdown.
69
- * @param {object} prompt The prompt object read from data attributes.
70
- */
71
- function selectPrompt(prompt) {
72
- selectedPrompt = prompt;
73
-
74
- // Update the dropdown button to show the selected prompt's description
75
- $('#prompt-select-button').text(prompt.description).addClass('item-selected');
76
- $('#clear-selection-button').show();
77
-
78
- // Clear the main textarea, as we are now in "prompt mode"
79
- $('#question').val('');
80
- autoResizeTextarea($('#question')[0]); // Reset height after clearing
81
-
82
- // Store values in hidden fields for backward compatibility or other uses
83
- $('#prompt-select-value').val(prompt.prompt);
84
- $('#prompt-select-description').val(prompt.description);
85
-
86
- // Render the dynamic input fields required by the selected prompt
87
- renderDynamicInputs(prompt.custom_fields || []);
88
- updateSendButtonState();
89
- }
90
-
91
- /**
92
- * Resets the prompt selection and clears associated UI elements.
93
- */
94
- function resetPromptSelection() {
95
- selectedPrompt = null;
96
-
97
- $('#prompt-select-button').text('Prompts disponibles ....').removeClass('item-selected');
98
- $('#clear-selection-button').hide();
99
- $('#prompt-select-value').val('');
100
- $('#prompt-select-description').val('');
101
-
102
- // Clear any dynamically generated input fields
103
- $('#dynamic-inputs-container').empty();
104
- }
105
-
106
- /**
107
- * Renders the custom input fields for the selected prompt.
108
- * @param {Array<object>} fields The array of custom field configurations.
109
- */
110
- function renderDynamicInputs(fields) {
111
- const container = $('#dynamic-inputs-container');
112
- container.empty();
113
-
114
- const row = $('<div class="row g-2"></div>');
115
- fields.forEach(field => {
116
- const colDiv = $('<div class="col-md"></div>');
117
- const formFloating = $('<div class="form-floating"></div>');
118
- const input = $(`<input type="${field.type || 'text'}" class="form-control form-control-soft" id="${field.data_key}-id" ">`);
119
- const label = $(`<label for="${field.data_key}-id">${field.label}</label>`);
120
-
121
- formFloating.append(input, label);
122
- colDiv.append(formFloating);
123
- row.append(colDiv);
124
- });
125
-
126
- container.append(row);
127
- }
128
58
 
59
+ });
129
60
 
130
61
 
131
62
  /**
@@ -163,7 +94,6 @@ const handleChatMessage = async function () {
163
94
  }
164
95
 
165
96
  // Simplificado: Si no hay mensaje, el 'finally' se encargará de limpiar.
166
- // Simplemente salimos de la función.
167
97
  if (!displayMessage) {
168
98
  return;
169
99
  }
@@ -269,28 +199,6 @@ const toggleSendStopButtons = function (showStop) {
269
199
  $('#stop-button-container').toggle(showStop);
270
200
  };
271
201
 
272
- /**
273
- * Resets the prompt selector to its default state.
274
- */
275
- function resetPromptSelect() {
276
- $('#prompt-select-button').text('Prompts disponibles ....').removeClass('item-selected');
277
- $('#prompt-select-value').val('');
278
- $('#prompt-select-description').val('');
279
- $('#clear-selection-button').hide();
280
- }
281
-
282
- /**
283
- * Resets the company-specific data input field.
284
- */
285
- function resetSpecificDataInput() {
286
- if (specificDataConfig && specificDataConfig.enabled) {
287
- const input = $('#' + specificDataConfig.id);
288
- input.val('').removeClass('has-content');
289
- $('#clear-' + specificDataConfig.id + '-button').hide();
290
- }
291
- }
292
-
293
-
294
202
  /**
295
203
  * Generic function to make API calls to the backend.
296
204
  * @param {string} apiPath - The API endpoint path.
@@ -321,21 +229,12 @@ const callToolkit = async function(apiPath, data, method, timeoutMs = 500000) {
321
229
 
322
230
  }
323
231
  const response = await fetch(url, fetchOptions);
324
-
325
232
  clearTimeout(timeoutId);
326
-
327
233
  if (!response.ok) {
328
- if (response.status === 401) {
329
- const errorMessage = `Tu sesión ha expirado. `;
330
- const errorIcon = '<i class="bi bi-exclamation-triangle"></i>';
331
- const infrastructureError = $('<div>').addClass('error-section').html(errorIcon + `<p>${errorMessage}</p>`);
332
- displayBotMessage(infrastructureError);
333
- return null;
334
- }
335
234
  try {
336
235
  // Intentamos leer el error como JSON, que es el formato esperado de nuestra API.
337
236
  const errorData = await response.json();
338
- const errorMessage = errorData.error_message || 'Error desconocido del servidor.';
237
+ const errorMessage = errorData.error_message || t_js('unknown_server_error'); // <-- Translation
339
238
  const errorIcon = '<i class="bi bi-exclamation-triangle"></i>';
340
239
  const endpointError = $('<div>').addClass('error-section').html(errorIcon + `<p>${errorMessage}</p>`);
341
240
  displayBotMessage(endpointError);
@@ -343,9 +242,7 @@ const callToolkit = async function(apiPath, data, method, timeoutMs = 500000) {
343
242
  // Si response.json() falla, es porque el cuerpo no era JSON (ej. un 502 con HTML).
344
243
  // Mostramos un error genérico y más claro para el usuario.
345
244
  const errorMessage = `Error de comunicación con el servidor (${response.status}). Por favor, intente de nuevo más tarde.`;
346
- const errorIcon = '<i class="bi bi-exclamation-triangle"></i>';
347
- const infrastructureError = $('<div>').addClass('error-section').html(errorIcon + `<p>${errorMessage}</p>`);
348
- displayBotMessage(infrastructureError);
245
+ toastr.error(errorMessage);
349
246
  }
350
247
  return null;
351
248
  }
@@ -355,18 +252,7 @@ const callToolkit = async function(apiPath, data, method, timeoutMs = 500000) {
355
252
  if (error.name === 'AbortError') {
356
253
  throw error; // Re-throw to be handled by handleChatMessage
357
254
  } else {
358
- // Log detallado en consola
359
- console.error('Error de red en callToolkit:', {
360
- url,
361
- method,
362
- error,
363
- message: error?.message,
364
- stack: error?.stack,
365
- });
366
- const friendlyMessage = "Ocurrió un error de red. Por favor, inténtalo de nuevo en unos momentos.";
367
- const errorIcon = '<i class="bi bi-exclamation-triangle"></i>';
368
- const commError = $('<div>').addClass('error-section').html(errorIcon + `<p>${friendlyMessage}</p>`);
369
- displayBotMessage(commError);
255
+ toastr.error(t_js('network_error') );
370
256
  }
371
257
  return null;
372
258
  }
@@ -387,11 +273,13 @@ const displayUserMessage = function(message, isEditable, originalQuestion) {
387
273
  userMessage.append(messageText);
388
274
 
389
275
  if (isEditable) {
390
- const editIcon = $('<i>').addClass('p-2 bi bi-pencil-fill edit-icon').attr('title', 'Edit query').on('click', function () {
391
- $('#question').val(originalQuestion).focus();
276
+ const editIcon = $('<i>').addClass('p-2 bi bi-pencil-fill edit-icon edit-pencil').attr('title', 'Edit query').on('click', function () {
277
+ $('#question').val(originalQuestion)
392
278
  autoResizeTextarea($('#question')[0]);
393
-
394
279
  $('#send-button').removeClass('disabled');
280
+
281
+ if (window.innerWidth > 768)
282
+ $('#question').focus();
395
283
  });
396
284
  userMessage.append(editIcon);
397
285
  }
@@ -424,12 +312,13 @@ const abortCurrentRequest = function () {
424
312
  const showSpinner = function () {
425
313
  if ($('#spinner').length) return;
426
314
  const accessibilityClass = (typeof bootstrap !== 'undefined') ? 'visually-hidden' : 'sr-only';
315
+ const spinnerText = t_js('loading');
427
316
  const spinner = $(`
428
317
  <div id="spinner" style="display: flex; align-items: center; justify-content: start; margin: 10px 0; padding: 10px;">
429
- <div class="spinner-border text-primary" role="status" style="width: 1.5rem; height: 1.5rem; margin-right: 15px;">
318
+ <div class="spinner-border" role="status" style="width: 1.5rem; height: 1.5rem; margin-right: 15px;">
430
319
  <span class="${accessibilityClass}">Loading...</span>
431
320
  </div>
432
- <span style="font-weight: bold; font-size: 15px;">Cargando...</span>
321
+ <span style="font-weight: bold; font-size: 15px;">${spinnerText}</span>
433
322
  </div>
434
323
  `);
435
324
  $('#chat-container').append(spinner).scrollTop($('#chat-container')[0].scrollHeight);
@@ -458,44 +347,3 @@ function toBase64(file) {
458
347
  });
459
348
  }
460
349
 
461
- /**
462
- * Displays the document validation results.
463
- * @param {Array<object>} document_list
464
- */
465
- function display_document_validation(document_list) {
466
- const requiredFields = ['document_name', 'document_type', 'causes', 'is_valid'];
467
- for (const doc of document_list) {
468
- if (!requiredFields.every(field => field in doc)) {
469
- console.warn("Document with incorrect structure:", doc);
470
- continue;
471
- }
472
- const docValidationSection = $('<div>').addClass('document-section card mt-2 mb-2');
473
- const cardBody = $('<div>').addClass('card-body');
474
- const headerDiv = $('<div>').addClass('d-flex justify-content-between align-items-center mb-2');
475
- const filenameSpan = $(`
476
- <div>
477
- <span class="text-primary fw-bold">File: </span>
478
- <span class="text-secondary">${doc.document_name}</span>
479
- </div>`);
480
- const badge_style = doc.is_valid ? 'bg-success' : 'bg-danger';
481
- const documentBadge = $('<span>')
482
- .addClass(`badge ${badge_style} p-2`)
483
- .text(doc.document_type);
484
- headerDiv.append(filenameSpan).append(documentBadge);
485
- cardBody.append(headerDiv);
486
-
487
- if (!doc.is_valid && doc.causes && doc.causes.length > 0) {
488
- const rejectionSection = $('<div>').addClass('rejection-reasons mt-2');
489
- rejectionSection.append('<h6 class="text-danger">Rejection Causes:</h6>');
490
- const causesList = doc.causes.map(cause => `<li class="text-secondary">${cause}</li>`).join('');
491
- rejectionSection.append(`<ul class="list-unstyled">${causesList}</ul>`);
492
- cardBody.append(rejectionSection);
493
- } else if (doc.is_valid) {
494
- const validSection = $('<div>').addClass('mt-2');
495
- validSection.append('<p class="text-success fw-bold">Valid document.</p>');
496
- cardBody.append(validSection);
497
- }
498
- docValidationSection.append(cardBody);
499
- displayBotMessage(docValidationSection);
500
- }
501
- }
@@ -1,4 +1,3 @@
1
- // static/js/chat_onboarding.js
2
1
  (function (global) {
3
2
  function qs(root, sel) { return (typeof sel === 'string') ? root.querySelector(sel) : sel; }
4
3
 
@@ -0,0 +1,94 @@
1
+ $(document).ready(function () {
2
+ // --- PROMPT ASSISTANT FUNCTIONALITY ---
3
+ const $promptCollapse = $('#prompt-assistant-collapse');
4
+
5
+ if ($promptCollapse.length) {
6
+ $promptCollapse.on('shown.bs.collapse', function () {
7
+ // Scroll to bottom smoothly when the collapse is shown
8
+ $('html, body').animate(
9
+ { scrollTop: $(document).height() },
10
+ 'slow'
11
+ );
12
+ });
13
+ }
14
+
15
+ $('.input-area').on('click', '.dropdown-menu a.dropdown-item', function (event) {
16
+ event.preventDefault();
17
+ const promptData = $(this).data();
18
+
19
+ const promptObject = {
20
+ prompt: promptData.promptName,
21
+ description: promptData.promptDescription,
22
+ custom_fields: typeof promptData.customFields === 'string' ? JSON.parse(promptData.customFields) : promptData.customFields
23
+ };
24
+ selectPrompt(promptObject);
25
+ });
26
+
27
+ // Handles the 'clear' button for the prompt selector
28
+ $('#clear-selection-button').on('click', function() {
29
+ resetPromptSelection();
30
+ updateSendButtonState();
31
+ });
32
+ });
33
+
34
+ /**
35
+ * Handles the selection of a prompt from the dropdown.
36
+ * @param {object} prompt The prompt object read from data attributes.
37
+ */
38
+ function selectPrompt(prompt) {
39
+ selectedPrompt = prompt;
40
+
41
+ // Update the dropdown button to show the selected prompt's description
42
+ $('#prompt-select-button').text(prompt.description).addClass('item-selected');
43
+ $('#clear-selection-button').show();
44
+
45
+ // Clear the main textarea, as we are now in "prompt mode"
46
+ $('#question').val('');
47
+ autoResizeTextarea($('#question')[0]); // Reset height after clearing
48
+
49
+ // Store values in hidden fields for backward compatibility or other uses
50
+ $('#prompt-select-value').val(prompt.prompt);
51
+ $('#prompt-select-description').val(prompt.description);
52
+
53
+ // Render the dynamic input fields required by the selected prompt
54
+ renderDynamicInputs(prompt.custom_fields || []);
55
+ updateSendButtonState();
56
+ }
57
+
58
+ /**
59
+ * Resets the prompt selection and clears associated UI elements.
60
+ */
61
+ function resetPromptSelection() {
62
+ selectedPrompt = null;
63
+
64
+ $('#prompt-select-button').text('Prompts disponibles ....').removeClass('item-selected');
65
+ $('#clear-selection-button').hide();
66
+ $('#prompt-select-value').val('');
67
+ $('#prompt-select-description').val('');
68
+
69
+ // Clear any dynamically generated input fields
70
+ $('#dynamic-inputs-container').empty();
71
+ }
72
+
73
+ /**
74
+ * Renders the custom input fields for the selected prompt.
75
+ * @param {Array<object>} fields The array of custom field configurations.
76
+ */
77
+ function renderDynamicInputs(fields) {
78
+ const container = $('#dynamic-inputs-container');
79
+ container.empty();
80
+
81
+ const row = $('<div class="row g-2"></div>');
82
+ fields.forEach(field => {
83
+ const colDiv = $('<div class="col-md"></div>');
84
+ const formFloating = $('<div class="form-floating"></div>');
85
+ const input = $(`<input type="${field.type || 'text'}" class="form-control form-control-soft" id="${field.data_key}-id" ">`);
86
+ const label = $(`<label for="${field.data_key}-id">${field.label}</label>`);
87
+
88
+ formFloating.append(input, label);
89
+ colDiv.append(formFloating);
90
+ row.append(colDiv);
91
+ });
92
+
93
+ container.append(row);
94
+ }
@@ -0,0 +1,35 @@
1
+ $(document).ready(function () {
2
+ $('#force-reload-button').on('click', function() {
3
+ reloadButton(this);
4
+ });
5
+
6
+ async function reloadButton(button) {
7
+ const originalIconClass = 'bi bi-arrow-clockwise';
8
+ const spinnerIconClass = 'spinner-border spinner-border-sm';
9
+
10
+ // Configuración de Toastr para que aparezca abajo a la derecha
11
+ toastr.options = {"positionClass": "toast-bottom-right", "preventDuplicates": true};
12
+
13
+ // 1. Deshabilitar y mostrar spinner
14
+ button.disabled = true;
15
+ const icon = button.querySelector('i');
16
+ icon.className = spinnerIconClass;
17
+ toastr.info(t_js('reload_init'));
18
+
19
+ // 2. prepare the api parameters
20
+ const apiPath = '/api/init-context';
21
+ const payload = {'user_identifier': window.user_identifier};
22
+
23
+ // 3. make the call to callToolkit
24
+ const data = await callToolkit(apiPath, payload, 'POST');
25
+ if (data) {
26
+ if (data.status === 'OK')
27
+ toastr.success(data.message || 'Contexto reloaded.');
28
+ else
29
+ toastr.error(data.error_message || 'error during reload');
30
+ }
31
+
32
+ button.disabled = false;
33
+ icon.className = originalIconClass;
34
+ }
35
+ });