iatoolkit 0.11.0__py3-none-any.whl → 0.66.2__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.
- iatoolkit/base_company.py +11 -3
- iatoolkit/common/routes.py +92 -52
- iatoolkit/common/session_manager.py +0 -1
- iatoolkit/common/util.py +17 -27
- iatoolkit/iatoolkit.py +91 -47
- iatoolkit/infra/llm_client.py +7 -8
- iatoolkit/infra/openai_adapter.py +1 -1
- iatoolkit/infra/redis_session_manager.py +48 -2
- iatoolkit/locales/en.yaml +144 -0
- iatoolkit/locales/es.yaml +140 -0
- iatoolkit/repositories/database_manager.py +17 -2
- iatoolkit/repositories/models.py +31 -4
- iatoolkit/repositories/profile_repo.py +7 -2
- iatoolkit/services/auth_service.py +193 -0
- iatoolkit/services/branding_service.py +59 -18
- iatoolkit/services/dispatcher_service.py +10 -40
- iatoolkit/services/excel_service.py +15 -15
- iatoolkit/services/help_content_service.py +30 -0
- iatoolkit/services/history_service.py +2 -11
- iatoolkit/services/i18n_service.py +104 -0
- iatoolkit/services/jwt_service.py +15 -24
- iatoolkit/services/language_service.py +77 -0
- iatoolkit/services/onboarding_service.py +43 -0
- iatoolkit/services/profile_service.py +148 -75
- iatoolkit/services/query_service.py +124 -81
- iatoolkit/services/tasks_service.py +1 -1
- iatoolkit/services/user_feedback_service.py +68 -32
- iatoolkit/services/user_session_context_service.py +112 -54
- iatoolkit/static/images/fernando.jpeg +0 -0
- iatoolkit/static/js/chat_feedback_button.js +80 -0
- iatoolkit/static/js/chat_help_content.js +124 -0
- iatoolkit/static/js/chat_history_button.js +112 -0
- iatoolkit/static/js/chat_logout_button.js +36 -0
- iatoolkit/static/js/chat_main.js +148 -220
- iatoolkit/static/js/chat_onboarding_button.js +97 -0
- iatoolkit/static/js/chat_prompt_manager.js +94 -0
- iatoolkit/static/js/chat_reload_button.js +35 -0
- iatoolkit/static/styles/chat_iatoolkit.css +367 -199
- iatoolkit/static/styles/chat_modal.css +98 -76
- iatoolkit/static/styles/chat_public.css +107 -0
- iatoolkit/static/styles/landing_page.css +182 -0
- iatoolkit/static/styles/onboarding.css +169 -0
- iatoolkit/system_prompts/query_main.prompt +3 -12
- iatoolkit/templates/_company_header.html +20 -0
- iatoolkit/templates/_login_widget.html +42 -0
- iatoolkit/templates/base.html +40 -20
- iatoolkit/templates/change_password.html +57 -36
- iatoolkit/templates/chat.html +169 -83
- iatoolkit/templates/chat_modals.html +134 -68
- iatoolkit/templates/error.html +44 -8
- iatoolkit/templates/forgot_password.html +40 -23
- iatoolkit/templates/index.html +145 -0
- iatoolkit/templates/login_simulation.html +34 -0
- iatoolkit/templates/onboarding_shell.html +104 -0
- iatoolkit/templates/signup.html +63 -65
- iatoolkit/views/base_login_view.py +92 -0
- iatoolkit/views/change_password_view.py +56 -30
- iatoolkit/views/external_login_view.py +61 -28
- iatoolkit/views/{file_store_view.py → file_store_api_view.py} +9 -2
- iatoolkit/views/forgot_password_view.py +27 -19
- iatoolkit/views/help_content_api_view.py +54 -0
- iatoolkit/views/history_api_view.py +56 -0
- iatoolkit/views/home_view.py +50 -23
- iatoolkit/views/index_view.py +14 -0
- iatoolkit/views/init_context_api_view.py +73 -0
- iatoolkit/views/llmquery_api_view.py +57 -0
- iatoolkit/views/login_simulation_view.py +81 -0
- iatoolkit/views/login_view.py +130 -37
- iatoolkit/views/logout_api_view.py +49 -0
- iatoolkit/views/profile_api_view.py +46 -0
- iatoolkit/views/{prompt_view.py → prompt_api_view.py} +10 -10
- iatoolkit/views/signup_view.py +42 -35
- iatoolkit/views/{tasks_view.py → tasks_api_view.py} +10 -36
- iatoolkit/views/tasks_review_api_view.py +55 -0
- iatoolkit/views/user_feedback_api_view.py +60 -0
- iatoolkit/views/verify_user_view.py +35 -28
- {iatoolkit-0.11.0.dist-info → iatoolkit-0.66.2.dist-info}/METADATA +2 -2
- iatoolkit-0.66.2.dist-info/RECORD +119 -0
- iatoolkit/common/auth.py +0 -200
- iatoolkit/static/images/arrow_up.png +0 -0
- iatoolkit/static/images/diagrama_iatoolkit.jpg +0 -0
- iatoolkit/static/images/logo_clinica.png +0 -0
- iatoolkit/static/images/logo_iatoolkit.png +0 -0
- iatoolkit/static/images/logo_maxxa.png +0 -0
- iatoolkit/static/images/logo_notaria.png +0 -0
- iatoolkit/static/images/logo_tarjeta.png +0 -0
- iatoolkit/static/images/logo_umayor.png +0 -0
- iatoolkit/static/images/upload.png +0 -0
- iatoolkit/static/js/chat_feedback.js +0 -115
- iatoolkit/static/js/chat_history.js +0 -117
- iatoolkit/static/styles/chat_info.css +0 -53
- iatoolkit/templates/header.html +0 -31
- iatoolkit/templates/home.html +0 -199
- iatoolkit/templates/login.html +0 -43
- iatoolkit/templates/test.html +0 -9
- iatoolkit/views/chat_token_request_view.py +0 -98
- iatoolkit/views/chat_view.py +0 -58
- iatoolkit/views/download_file_view.py +0 -58
- iatoolkit/views/external_chat_login_view.py +0 -95
- iatoolkit/views/history_view.py +0 -57
- iatoolkit/views/llmquery_view.py +0 -65
- iatoolkit/views/tasks_review_view.py +0 -83
- iatoolkit/views/user_feedback_view.py +0 -74
- iatoolkit-0.11.0.dist-info/RECORD +0 -110
- {iatoolkit-0.11.0.dist-info → iatoolkit-0.66.2.dist-info}/WHEEL +0 -0
- {iatoolkit-0.11.0.dist-info → iatoolkit-0.66.2.dist-info}/top_level.txt +0 -0
iatoolkit/static/js/chat_main.js
CHANGED
|
@@ -1,38 +1,41 @@
|
|
|
1
1
|
// Global variables for request management
|
|
2
|
-
let currentAbortController = null;
|
|
3
2
|
let isRequestInProgress = false;
|
|
4
|
-
|
|
3
|
+
let abortController = null;
|
|
5
4
|
let selectedPrompt = null; // Will hold a lightweight prompt object
|
|
6
5
|
|
|
7
6
|
$(document).ready(function () {
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
// Si viene un Token retornado por login con APY-KEY se gatilla el redeem a una sesion de flask
|
|
8
|
+
if (window.redeemToken) {
|
|
9
|
+
const url = '/api/redeem_token';
|
|
10
|
+
// No await: dejamos que callToolkit maneje todo internamente
|
|
11
|
+
callToolkit(url, {'token': window.redeemToken}, "POST").catch(() => {});
|
|
12
|
+
}
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
event.preventDefault();
|
|
15
|
-
const promptData = $(this).data();
|
|
14
|
+
const layoutContainer = document.querySelector('.chat-layout-container');
|
|
15
|
+
const promptAssistantCollapse = document.getElementById('prompt-assistant-collapse');
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
+
});
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
promptAssistantCollapse.addEventListener('hide.bs.collapse', function () {
|
|
26
|
+
layoutContainer.classList.remove('prompt-assistant-open');
|
|
27
|
+
});
|
|
28
|
+
}
|
|
25
29
|
|
|
26
|
-
//
|
|
27
|
-
$('#
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
// --- chat main event hadlers ---
|
|
31
|
+
$('#send-button').on('click', handleChatMessage);
|
|
32
|
+
$('#stop-button').on('click', abortCurrentRequest);
|
|
33
|
+
if (window.sendButtonColor)
|
|
34
|
+
$('#send-button i').css('color', window.sendButtonColor);
|
|
31
35
|
|
|
32
|
-
// --- TEXTAREA FUNCTIONALITY ---
|
|
33
|
-
const questionTextarea = $('#question');
|
|
34
36
|
|
|
35
37
|
// Handles Enter key press to send a message
|
|
38
|
+
const questionTextarea = $('#question');
|
|
36
39
|
questionTextarea.on('keypress', function (event) {
|
|
37
40
|
if (event.key === 'Enter' && !event.shiftKey) {
|
|
38
41
|
event.preventDefault();
|
|
@@ -52,141 +55,101 @@ $(document).ready(function () {
|
|
|
52
55
|
|
|
53
56
|
// Set the initial disabled state of the send button
|
|
54
57
|
updateSendButtonState();
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Handles the selection of a prompt from the dropdown.
|
|
60
|
-
* @param {object} prompt The prompt object read from data attributes.
|
|
61
|
-
*/
|
|
62
|
-
function selectPrompt(prompt) {
|
|
63
|
-
selectedPrompt = prompt;
|
|
64
|
-
|
|
65
|
-
// Update the dropdown button to show the selected prompt's description
|
|
66
|
-
$('#prompt-select-button').text(prompt.description).addClass('item-selected');
|
|
67
|
-
$('#clear-selection-button').show();
|
|
68
|
-
|
|
69
|
-
// Clear the main textarea, as we are now in "prompt mode"
|
|
70
|
-
$('#question').val('');
|
|
71
|
-
autoResizeTextarea($('#question')[0]); // Reset height after clearing
|
|
72
|
-
|
|
73
|
-
// Store values in hidden fields for backward compatibility or other uses
|
|
74
|
-
$('#prompt-select-value').val(prompt.prompt);
|
|
75
|
-
$('#prompt-select-description').val(prompt.description);
|
|
76
|
-
|
|
77
|
-
// Render the dynamic input fields required by the selected prompt
|
|
78
|
-
renderDynamicInputs(prompt.custom_fields || []);
|
|
79
|
-
updateSendButtonState();
|
|
80
|
-
}
|
|
81
58
|
|
|
82
|
-
|
|
83
|
-
* Resets the prompt selection and clears associated UI elements.
|
|
84
|
-
*/
|
|
85
|
-
function resetPromptSelection() {
|
|
86
|
-
selectedPrompt = null;
|
|
87
|
-
|
|
88
|
-
$('#prompt-select-button').text('Prompts disponibles ....').removeClass('item-selected');
|
|
89
|
-
$('#clear-selection-button').hide();
|
|
90
|
-
$('#prompt-select-value').val('');
|
|
91
|
-
$('#prompt-select-description').val('');
|
|
92
|
-
|
|
93
|
-
// Clear any dynamically generated input fields
|
|
94
|
-
$('#dynamic-inputs-container').empty();
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Renders the custom input fields for the selected prompt.
|
|
99
|
-
* @param {Array<object>} fields The array of custom field configurations.
|
|
100
|
-
*/
|
|
101
|
-
function renderDynamicInputs(fields) {
|
|
102
|
-
const container = $('#dynamic-inputs-container');
|
|
103
|
-
container.empty();
|
|
104
|
-
|
|
105
|
-
const row = $('<div class="row g-2"></div>');
|
|
106
|
-
fields.forEach(field => {
|
|
107
|
-
const colDiv = $('<div class="col-md"></div>');
|
|
108
|
-
const formFloating = $('<div class="form-floating"></div>');
|
|
109
|
-
const input = $(`<input type="${field.type || 'text'}" class="form-control form-control-soft" id="${field.data_key}-id" ">`);
|
|
110
|
-
const label = $(`<label for="${field.data_key}-id">${field.label}</label>`);
|
|
111
|
-
|
|
112
|
-
formFloating.append(input, label);
|
|
113
|
-
colDiv.append(formFloating);
|
|
114
|
-
row.append(colDiv);
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
container.append(row);
|
|
118
|
-
}
|
|
59
|
+
});
|
|
119
60
|
|
|
120
61
|
|
|
121
62
|
/**
|
|
122
63
|
* Main function to handle sending a chat message.
|
|
123
64
|
*/
|
|
124
65
|
const handleChatMessage = async function () {
|
|
125
|
-
if (isRequestInProgress || $('#send-button').hasClass('disabled'))
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const promptName = selectedPrompt ? selectedPrompt.prompt : null;
|
|
66
|
+
if (isRequestInProgress || $('#send-button').hasClass('disabled')) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
129
69
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const clientData = {};
|
|
70
|
+
isRequestInProgress = true;
|
|
71
|
+
toggleSendStopButtons(true);
|
|
133
72
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
73
|
+
try {
|
|
74
|
+
const question = $('#question').val().trim();
|
|
75
|
+
const promptName = selectedPrompt ? selectedPrompt.prompt : null;
|
|
137
76
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
clientData[field.data_key] = value;
|
|
142
|
-
}
|
|
143
|
-
});
|
|
77
|
+
let displayMessage = question;
|
|
78
|
+
let isEditable = true;
|
|
79
|
+
const clientData = {};
|
|
144
80
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
81
|
+
if (selectedPrompt) {
|
|
82
|
+
displayMessage = selectedPrompt.description;
|
|
83
|
+
isEditable = false;
|
|
84
|
+
|
|
85
|
+
(selectedPrompt.custom_fields || []).forEach(field => {
|
|
86
|
+
const value = $('#' + field.data_key + '-id').val().trim();
|
|
87
|
+
if (value) {
|
|
88
|
+
clientData[field.data_key] = value;
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const paramsString = Object.values(clientData).join(', ');
|
|
93
|
+
if (paramsString) { displayMessage += `: ${paramsString}`; }
|
|
149
94
|
}
|
|
150
|
-
}
|
|
151
95
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
showSpinner();
|
|
157
|
-
toggleSendStopButtons(true);
|
|
96
|
+
// Simplificado: Si no hay mensaje, el 'finally' se encargará de limpiar.
|
|
97
|
+
if (!displayMessage) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
158
100
|
|
|
159
|
-
|
|
101
|
+
displayUserMessage(displayMessage, isEditable, question);
|
|
102
|
+
showSpinner();
|
|
103
|
+
resetAllInputs();
|
|
160
104
|
|
|
161
|
-
|
|
162
|
-
|
|
105
|
+
const files = window.filePond.getFiles();
|
|
106
|
+
const filesBase64 = await Promise.all(files.map(fileItem => toBase64(fileItem.file)));
|
|
163
107
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
};
|
|
108
|
+
const data = {
|
|
109
|
+
question: question,
|
|
110
|
+
prompt_name: promptName,
|
|
111
|
+
client_data: clientData,
|
|
112
|
+
files: filesBase64.map(f => ({ filename: f.name, content: f.base64 })),
|
|
113
|
+
user_identifier: window.user_identifier
|
|
114
|
+
};
|
|
172
115
|
|
|
173
|
-
|
|
174
|
-
const responseData = await callLLMAPI("/llm_query", data, "POST");
|
|
116
|
+
const responseData = await callToolkit("/api/llm_query", data, "POST");
|
|
175
117
|
if (responseData && responseData.answer) {
|
|
176
118
|
const answerSection = $('<div>').addClass('answer-section llm-output').append(responseData.answer);
|
|
177
119
|
displayBotMessage(answerSection);
|
|
178
120
|
}
|
|
179
121
|
} catch (error) {
|
|
180
|
-
|
|
181
|
-
|
|
122
|
+
if (error.name === 'AbortError') {
|
|
123
|
+
console.log('Petición abortada por el usuario.');
|
|
124
|
+
|
|
125
|
+
// Usando jQuery estándar para construir el elemento ---
|
|
126
|
+
const icon = $('<i>').addClass('bi bi-stop-circle me-2'); // Icono sin "fill" para un look más ligero
|
|
127
|
+
const textSpan = $('<span>').text('La generación de la respuesta ha sido detenida.');
|
|
128
|
+
|
|
129
|
+
const abortMessage = $('<div>')
|
|
130
|
+
.addClass('system-message')
|
|
131
|
+
.append(icon)
|
|
132
|
+
.append(textSpan);
|
|
133
|
+
|
|
134
|
+
displayBotMessage(abortMessage);
|
|
135
|
+
} else {
|
|
136
|
+
console.error("Error in handleChatMessage:", error);
|
|
137
|
+
const errorSection = $('<div>').addClass('error-section').append('<p>Ocurrió un error al procesar la solicitud.</p>');
|
|
138
|
+
displayBotMessage(errorSection);
|
|
139
|
+
}
|
|
182
140
|
} finally {
|
|
141
|
+
// Este bloque se ejecuta siempre, garantizando que el estado se limpie.
|
|
142
|
+
isRequestInProgress = false;
|
|
183
143
|
hideSpinner();
|
|
184
144
|
toggleSendStopButtons(false);
|
|
185
145
|
updateSendButtonState();
|
|
186
|
-
window.filePond
|
|
146
|
+
if (window.filePond) {
|
|
147
|
+
window.filePond.removeFiles();
|
|
148
|
+
}
|
|
187
149
|
}
|
|
188
150
|
};
|
|
189
151
|
|
|
152
|
+
|
|
190
153
|
/**
|
|
191
154
|
* Resets all inputs to their initial state.
|
|
192
155
|
*/
|
|
@@ -236,28 +199,6 @@ const toggleSendStopButtons = function (showStop) {
|
|
|
236
199
|
$('#stop-button-container').toggle(showStop);
|
|
237
200
|
};
|
|
238
201
|
|
|
239
|
-
/**
|
|
240
|
-
* Resets the prompt selector to its default state.
|
|
241
|
-
*/
|
|
242
|
-
function resetPromptSelect() {
|
|
243
|
-
$('#prompt-select-button').text('Prompts disponibles ....').removeClass('item-selected');
|
|
244
|
-
$('#prompt-select-value').val('');
|
|
245
|
-
$('#prompt-select-description').val('');
|
|
246
|
-
$('#clear-selection-button').hide();
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Resets the company-specific data input field.
|
|
251
|
-
*/
|
|
252
|
-
function resetSpecificDataInput() {
|
|
253
|
-
if (specificDataConfig && specificDataConfig.enabled) {
|
|
254
|
-
const input = $('#' + specificDataConfig.id);
|
|
255
|
-
input.val('').removeClass('has-content');
|
|
256
|
-
$('#clear-' + specificDataConfig.id + '-button').hide();
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
|
|
261
202
|
/**
|
|
262
203
|
* Generic function to make API calls to the backend.
|
|
263
204
|
* @param {string} apiPath - The API endpoint path.
|
|
@@ -266,32 +207,47 @@ function resetSpecificDataInput() {
|
|
|
266
207
|
* @param {number} timeoutMs - Timeout in milliseconds.
|
|
267
208
|
* @returns {Promise<object|null>} The response data or null on error.
|
|
268
209
|
*/
|
|
269
|
-
const
|
|
210
|
+
const callToolkit = async function(apiPath, data, method, timeoutMs = 500000) {
|
|
270
211
|
const url = `${window.iatoolkit_base_url}/${window.companyShortName}${apiPath}`;
|
|
271
212
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
headers['X-Chat-Token'] = window.sessionJWT;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
const controller = new AbortController();
|
|
278
|
-
currentAbortController = controller;
|
|
279
|
-
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
213
|
+
abortController = new AbortController();
|
|
214
|
+
const timeoutId = setTimeout(() => abortController.abort(), timeoutMs);
|
|
280
215
|
|
|
281
216
|
try {
|
|
282
|
-
const
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
217
|
+
const fetchOptions = {
|
|
218
|
+
method: method,
|
|
219
|
+
signal: abortController.signal,
|
|
220
|
+
credentials: 'include'
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
// Solo agrega body si el método lo soporta y hay datos
|
|
224
|
+
const methodUpper = (method || '').toUpperCase();
|
|
225
|
+
const canHaveBody = !['GET', 'HEAD'].includes(methodUpper);
|
|
226
|
+
if (canHaveBody && data !== undefined && data !== null) {
|
|
227
|
+
fetchOptions.body = JSON.stringify(data);
|
|
228
|
+
fetchOptions.headers = {"Content-Type": "application/json"};
|
|
229
|
+
|
|
230
|
+
}
|
|
231
|
+
const response = await fetch(url, fetchOptions);
|
|
232
|
+
|
|
289
233
|
clearTimeout(timeoutId);
|
|
290
234
|
|
|
291
235
|
if (!response.ok) {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
236
|
+
try {
|
|
237
|
+
// Intentamos leer el error como JSON, que es el formato esperado de nuestra API.
|
|
238
|
+
const errorData = await response.json();
|
|
239
|
+
const errorMessage = errorData.error_message || t_js('unknown_server_error'); // <-- Translation
|
|
240
|
+
const errorIcon = '<i class="bi bi-exclamation-triangle"></i>';
|
|
241
|
+
const endpointError = $('<div>').addClass('error-section').html(errorIcon + `<p>${errorMessage}</p>`);
|
|
242
|
+
displayBotMessage(endpointError);
|
|
243
|
+
} catch (e) {
|
|
244
|
+
// Si response.json() falla, es porque el cuerpo no era JSON (ej. un 502 con HTML).
|
|
245
|
+
// Mostramos un error genérico y más claro para el usuario.
|
|
246
|
+
const errorMessage = `Error de comunicación con el servidor (${response.status}). Por favor, intente de nuevo más tarde.`;
|
|
247
|
+
const errorIcon = '<i class="bi bi-exclamation-triangle"></i>';
|
|
248
|
+
const infrastructureError = $('<div>').addClass('error-section').html(errorIcon + `<p>${errorMessage}</p>`);
|
|
249
|
+
displayBotMessage(infrastructureError);
|
|
250
|
+
}
|
|
295
251
|
return null;
|
|
296
252
|
}
|
|
297
253
|
return await response.json();
|
|
@@ -300,7 +256,17 @@ const callLLMAPI = async function(apiPath, data, method, timeoutMs = 500000) {
|
|
|
300
256
|
if (error.name === 'AbortError') {
|
|
301
257
|
throw error; // Re-throw to be handled by handleChatMessage
|
|
302
258
|
} else {
|
|
303
|
-
|
|
259
|
+
// Log detallado en consola
|
|
260
|
+
console.error('Network error in callToolkit:', {
|
|
261
|
+
url,
|
|
262
|
+
method,
|
|
263
|
+
error,
|
|
264
|
+
message: error?.message,
|
|
265
|
+
stack: error?.stack,
|
|
266
|
+
});
|
|
267
|
+
const friendlyMessage = t_js('network_error');
|
|
268
|
+
const errorIcon = '<i class="bi bi-exclamation-triangle"></i>';
|
|
269
|
+
const commError = $('<div>').addClass('error-section').html(errorIcon + `<p>${friendlyMessage}</p>`);
|
|
304
270
|
displayBotMessage(commError);
|
|
305
271
|
}
|
|
306
272
|
return null;
|
|
@@ -322,10 +288,13 @@ const displayUserMessage = function(message, isEditable, originalQuestion) {
|
|
|
322
288
|
userMessage.append(messageText);
|
|
323
289
|
|
|
324
290
|
if (isEditable) {
|
|
325
|
-
const editIcon = $('<i>').addClass('p-2 bi bi-pencil-fill edit-icon').attr('title', 'Edit query').on('click', function () {
|
|
326
|
-
$('#question').val(originalQuestion)
|
|
291
|
+
const editIcon = $('<i>').addClass('p-2 bi bi-pencil-fill edit-icon edit-pencil').attr('title', 'Edit query').on('click', function () {
|
|
292
|
+
$('#question').val(originalQuestion)
|
|
327
293
|
autoResizeTextarea($('#question')[0]);
|
|
328
|
-
|
|
294
|
+
$('#send-button').removeClass('disabled');
|
|
295
|
+
|
|
296
|
+
if (window.innerWidth > 768)
|
|
297
|
+
$('#question').focus();
|
|
329
298
|
});
|
|
330
299
|
userMessage.append(editIcon);
|
|
331
300
|
}
|
|
@@ -347,9 +316,8 @@ function displayBotMessage(section) {
|
|
|
347
316
|
* Aborts the current in-progress API request.
|
|
348
317
|
*/
|
|
349
318
|
const abortCurrentRequest = function () {
|
|
350
|
-
if (
|
|
351
|
-
|
|
352
|
-
currentAbortController.abort();
|
|
319
|
+
if (isRequestInProgress && abortController) {
|
|
320
|
+
abortController.abort();
|
|
353
321
|
}
|
|
354
322
|
};
|
|
355
323
|
|
|
@@ -359,12 +327,13 @@ const abortCurrentRequest = function () {
|
|
|
359
327
|
const showSpinner = function () {
|
|
360
328
|
if ($('#spinner').length) return;
|
|
361
329
|
const accessibilityClass = (typeof bootstrap !== 'undefined') ? 'visually-hidden' : 'sr-only';
|
|
330
|
+
const spinnerText = t_js('loading');
|
|
362
331
|
const spinner = $(`
|
|
363
332
|
<div id="spinner" style="display: flex; align-items: center; justify-content: start; margin: 10px 0; padding: 10px;">
|
|
364
|
-
<div class="spinner-border
|
|
333
|
+
<div class="spinner-border" role="status" style="width: 1.5rem; height: 1.5rem; margin-right: 15px;">
|
|
365
334
|
<span class="${accessibilityClass}">Loading...</span>
|
|
366
335
|
</div>
|
|
367
|
-
<span style="font-weight: bold; font-size: 15px;"
|
|
336
|
+
<span style="font-weight: bold; font-size: 15px;">${spinnerText}</span>
|
|
368
337
|
</div>
|
|
369
338
|
`);
|
|
370
339
|
$('#chat-container').append(spinner).scrollTop($('#chat-container')[0].scrollHeight);
|
|
@@ -393,44 +362,3 @@ function toBase64(file) {
|
|
|
393
362
|
});
|
|
394
363
|
}
|
|
395
364
|
|
|
396
|
-
/**
|
|
397
|
-
* Displays the document validation results.
|
|
398
|
-
* @param {Array<object>} document_list
|
|
399
|
-
*/
|
|
400
|
-
function display_document_validation(document_list) {
|
|
401
|
-
const requiredFields = ['document_name', 'document_type', 'causes', 'is_valid'];
|
|
402
|
-
for (const doc of document_list) {
|
|
403
|
-
if (!requiredFields.every(field => field in doc)) {
|
|
404
|
-
console.warn("Document with incorrect structure:", doc);
|
|
405
|
-
continue;
|
|
406
|
-
}
|
|
407
|
-
const docValidationSection = $('<div>').addClass('document-section card mt-2 mb-2');
|
|
408
|
-
const cardBody = $('<div>').addClass('card-body');
|
|
409
|
-
const headerDiv = $('<div>').addClass('d-flex justify-content-between align-items-center mb-2');
|
|
410
|
-
const filenameSpan = $(`
|
|
411
|
-
<div>
|
|
412
|
-
<span class="text-primary fw-bold">File: </span>
|
|
413
|
-
<span class="text-secondary">${doc.document_name}</span>
|
|
414
|
-
</div>`);
|
|
415
|
-
const badge_style = doc.is_valid ? 'bg-success' : 'bg-danger';
|
|
416
|
-
const documentBadge = $('<span>')
|
|
417
|
-
.addClass(`badge ${badge_style} p-2`)
|
|
418
|
-
.text(doc.document_type);
|
|
419
|
-
headerDiv.append(filenameSpan).append(documentBadge);
|
|
420
|
-
cardBody.append(headerDiv);
|
|
421
|
-
|
|
422
|
-
if (!doc.is_valid && doc.causes && doc.causes.length > 0) {
|
|
423
|
-
const rejectionSection = $('<div>').addClass('rejection-reasons mt-2');
|
|
424
|
-
rejectionSection.append('<h6 class="text-danger">Rejection Causes:</h6>');
|
|
425
|
-
const causesList = doc.causes.map(cause => `<li class="text-secondary">${cause}</li>`).join('');
|
|
426
|
-
rejectionSection.append(`<ul class="list-unstyled">${causesList}</ul>`);
|
|
427
|
-
cardBody.append(rejectionSection);
|
|
428
|
-
} else if (doc.is_valid) {
|
|
429
|
-
const validSection = $('<div>').addClass('mt-2');
|
|
430
|
-
validSection.append('<p class="text-success fw-bold">Valid document.</p>');
|
|
431
|
-
cardBody.append(validSection);
|
|
432
|
-
}
|
|
433
|
-
docValidationSection.append(cardBody);
|
|
434
|
-
displayBotMessage(docValidationSection);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
(function (global) {
|
|
2
|
+
function qs(root, sel) { return (typeof sel === 'string') ? root.querySelector(sel) : sel; }
|
|
3
|
+
|
|
4
|
+
function createDots(container, count, activeIdx, activeColor) {
|
|
5
|
+
container.innerHTML = '';
|
|
6
|
+
for (let i = 0; i < count; i++) {
|
|
7
|
+
const d = document.createElement('div');
|
|
8
|
+
if (i === activeIdx) d.classList.add('active');
|
|
9
|
+
d.style.width = '10px';
|
|
10
|
+
d.style.height = '10px';
|
|
11
|
+
d.style.borderRadius = '50%';
|
|
12
|
+
d.style.backgroundColor = i === activeIdx ? (activeColor || 'var(--brand-primary-color, #FF5100)') : '#ddd';
|
|
13
|
+
d.style.transition = 'background-color .3s';
|
|
14
|
+
container.appendChild(d);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function initOnboarding(opts) {
|
|
19
|
+
const {
|
|
20
|
+
mode = 'modal',
|
|
21
|
+
cards = [],
|
|
22
|
+
ui = {},
|
|
23
|
+
autoRotateMs = 5000,
|
|
24
|
+
shell = {}
|
|
25
|
+
} = opts;
|
|
26
|
+
|
|
27
|
+
const root = document;
|
|
28
|
+
const elIcon = qs(root, ui.icon);
|
|
29
|
+
const elTitle = qs(root, ui.title);
|
|
30
|
+
const elText = qs(root, ui.text);
|
|
31
|
+
const elDots = qs(root, ui.dots);
|
|
32
|
+
const elPrev = qs(root, ui.prev);
|
|
33
|
+
const elNext = qs(root, ui.next);
|
|
34
|
+
|
|
35
|
+
let idx = 0;
|
|
36
|
+
let autoTimer = null;
|
|
37
|
+
|
|
38
|
+
function hasCards() { return Array.isArray(cards) && cards.length > 0; }
|
|
39
|
+
|
|
40
|
+
function render() {
|
|
41
|
+
if (!hasCards()) return;
|
|
42
|
+
const c = cards[idx] || {};
|
|
43
|
+
if (elIcon) elIcon.innerHTML = `<i class="${c.icon || 'bi bi-lightbulb'}"></i>`;
|
|
44
|
+
if (elTitle) elTitle.textContent = c.title || '';
|
|
45
|
+
if (elText) elText.innerHTML = c.text || '';
|
|
46
|
+
if (elDots) createDots(elDots, cards.length, idx);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function next() { if (!hasCards()) return; idx = (idx + 1) % cards.length; render(); }
|
|
50
|
+
function prev() { if (!hasCards()) return; idx = (idx - 1 + cards.length) % cards.length; render(); }
|
|
51
|
+
|
|
52
|
+
function startAuto() {
|
|
53
|
+
stopAuto();
|
|
54
|
+
if (!hasCards()) return;
|
|
55
|
+
autoTimer = setInterval(next, autoRotateMs);
|
|
56
|
+
}
|
|
57
|
+
function stopAuto() { if (autoTimer) { clearInterval(autoTimer); autoTimer = null; } }
|
|
58
|
+
|
|
59
|
+
function setupShellIfNeeded() {
|
|
60
|
+
if (mode !== 'shell') return;
|
|
61
|
+
const loader = ui.loader ? qs(root, ui.loader) : null;
|
|
62
|
+
const container = ui.container ? qs(root, ui.container) : null;
|
|
63
|
+
if (!container || !shell.iframeSrc) return;
|
|
64
|
+
|
|
65
|
+
const iframe = document.createElement('iframe');
|
|
66
|
+
iframe.src = shell.iframeSrc;
|
|
67
|
+
iframe.style.width = '100%';
|
|
68
|
+
iframe.style.height = '100%';
|
|
69
|
+
iframe.style.border = 'none';
|
|
70
|
+
iframe.style.display = 'none';
|
|
71
|
+
|
|
72
|
+
iframe.onload = function () {
|
|
73
|
+
iframe.style.display = 'block';
|
|
74
|
+
if (loader) {
|
|
75
|
+
loader.style.opacity = '0';
|
|
76
|
+
setTimeout(() => loader.style.display = 'none', 500);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
container.appendChild(iframe);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (elPrev) elPrev.addEventListener('click', () => { prev(); startAuto(); });
|
|
83
|
+
if (elNext) elNext.addEventListener('click', () => { next(); startAuto(); });
|
|
84
|
+
|
|
85
|
+
function start() {
|
|
86
|
+
idx = 0;
|
|
87
|
+
render();
|
|
88
|
+
startAuto();
|
|
89
|
+
if (mode === 'shell') setupShellIfNeeded();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return { start, stop: stopAuto, next, prev, hasCards };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Export global
|
|
96
|
+
global.initOnboarding = initOnboarding;
|
|
97
|
+
})(window);
|
|
@@ -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
|
+
}
|