iatoolkit 0.51.0__py3-none-any.whl → 0.55.1__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.
- iatoolkit/common/routes.py +3 -2
- iatoolkit/iatoolkit.py +1 -1
- iatoolkit/static/js/chat_onboarding.js +98 -0
- iatoolkit/static/styles/chat_iatoolkit.css +0 -1
- iatoolkit/static/styles/onboarding.css +169 -0
- iatoolkit/templates/base.html +1 -0
- iatoolkit/templates/chat.html +59 -15
- iatoolkit/templates/chat_modals.html +33 -0
- iatoolkit/templates/login_test.html +93 -125
- iatoolkit/templates/onboarding_shell.html +101 -163
- iatoolkit/views/base_login_view.py +8 -3
- iatoolkit/views/login_simulation_view.py +60 -0
- iatoolkit/views/login_view.py +7 -2
- {iatoolkit-0.51.0.dist-info → iatoolkit-0.55.1.dist-info}/METADATA +1 -1
- {iatoolkit-0.51.0.dist-info → iatoolkit-0.55.1.dist-info}/RECORD +17 -15
- iatoolkit/views/login_test_view.py +0 -31
- {iatoolkit-0.51.0.dist-info → iatoolkit-0.55.1.dist-info}/WHEEL +0 -0
- {iatoolkit-0.51.0.dist-info → iatoolkit-0.55.1.dist-info}/top_level.txt +0 -0
iatoolkit/common/routes.py
CHANGED
|
@@ -25,7 +25,7 @@ def register_views(injector, app):
|
|
|
25
25
|
from iatoolkit.views.llmquery_api_view import LLMQueryApiView
|
|
26
26
|
from iatoolkit.views.tasks_view import TaskView
|
|
27
27
|
from iatoolkit.views.tasks_review_view import TaskReviewView
|
|
28
|
-
from iatoolkit.views.
|
|
28
|
+
from iatoolkit.views.login_simulation_view import LoginSimulationView
|
|
29
29
|
from iatoolkit.views.login_view import LoginView, FinalizeContextView
|
|
30
30
|
from iatoolkit.views.external_login_view import ExternalLoginView
|
|
31
31
|
from iatoolkit.views.signup_view import SignupView
|
|
@@ -112,7 +112,8 @@ def register_views(injector, app):
|
|
|
112
112
|
abort(404)
|
|
113
113
|
|
|
114
114
|
# login testing (old home page)
|
|
115
|
-
app.add_url_rule('/login_test',
|
|
115
|
+
app.add_url_rule('/login_test/<company_short_name>/<external_user_id>',
|
|
116
|
+
view_func=LoginSimulationView.as_view('login_test'))
|
|
116
117
|
|
|
117
118
|
app.add_url_rule(
|
|
118
119
|
'/about', # URL de la ruta
|
iatoolkit/iatoolkit.py
CHANGED
|
@@ -19,7 +19,7 @@ from werkzeug.middleware.proxy_fix import ProxyFix
|
|
|
19
19
|
from injector import Binder, singleton, Injector
|
|
20
20
|
from importlib.metadata import version as _pkg_version, PackageNotFoundError
|
|
21
21
|
|
|
22
|
-
IATOOLKIT_VERSION = "0.
|
|
22
|
+
IATOOLKIT_VERSION = "0.55.1"
|
|
23
23
|
|
|
24
24
|
# global variable for the unique instance of IAToolkit
|
|
25
25
|
_iatoolkit_instance: Optional['IAToolkit'] = None
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// static/js/chat_onboarding.js
|
|
2
|
+
(function (global) {
|
|
3
|
+
function qs(root, sel) { return (typeof sel === 'string') ? root.querySelector(sel) : sel; }
|
|
4
|
+
|
|
5
|
+
function createDots(container, count, activeIdx, activeColor) {
|
|
6
|
+
container.innerHTML = '';
|
|
7
|
+
for (let i = 0; i < count; i++) {
|
|
8
|
+
const d = document.createElement('div');
|
|
9
|
+
if (i === activeIdx) d.classList.add('active');
|
|
10
|
+
d.style.width = '10px';
|
|
11
|
+
d.style.height = '10px';
|
|
12
|
+
d.style.borderRadius = '50%';
|
|
13
|
+
d.style.backgroundColor = i === activeIdx ? (activeColor || 'var(--brand-primary-color, #FF5100)') : '#ddd';
|
|
14
|
+
d.style.transition = 'background-color .3s';
|
|
15
|
+
container.appendChild(d);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function initOnboarding(opts) {
|
|
20
|
+
const {
|
|
21
|
+
mode = 'modal',
|
|
22
|
+
cards = [],
|
|
23
|
+
ui = {},
|
|
24
|
+
autoRotateMs = 5000,
|
|
25
|
+
shell = {}
|
|
26
|
+
} = opts;
|
|
27
|
+
|
|
28
|
+
const root = document;
|
|
29
|
+
const elIcon = qs(root, ui.icon);
|
|
30
|
+
const elTitle = qs(root, ui.title);
|
|
31
|
+
const elText = qs(root, ui.text);
|
|
32
|
+
const elDots = qs(root, ui.dots);
|
|
33
|
+
const elPrev = qs(root, ui.prev);
|
|
34
|
+
const elNext = qs(root, ui.next);
|
|
35
|
+
|
|
36
|
+
let idx = 0;
|
|
37
|
+
let autoTimer = null;
|
|
38
|
+
|
|
39
|
+
function hasCards() { return Array.isArray(cards) && cards.length > 0; }
|
|
40
|
+
|
|
41
|
+
function render() {
|
|
42
|
+
if (!hasCards()) return;
|
|
43
|
+
const c = cards[idx] || {};
|
|
44
|
+
if (elIcon) elIcon.innerHTML = `<i class="${c.icon || 'bi bi-lightbulb'}"></i>`;
|
|
45
|
+
if (elTitle) elTitle.textContent = c.title || '';
|
|
46
|
+
if (elText) elText.innerHTML = c.text || '';
|
|
47
|
+
if (elDots) createDots(elDots, cards.length, idx);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function next() { if (!hasCards()) return; idx = (idx + 1) % cards.length; render(); }
|
|
51
|
+
function prev() { if (!hasCards()) return; idx = (idx - 1 + cards.length) % cards.length; render(); }
|
|
52
|
+
|
|
53
|
+
function startAuto() {
|
|
54
|
+
stopAuto();
|
|
55
|
+
if (!hasCards()) return;
|
|
56
|
+
autoTimer = setInterval(next, autoRotateMs);
|
|
57
|
+
}
|
|
58
|
+
function stopAuto() { if (autoTimer) { clearInterval(autoTimer); autoTimer = null; } }
|
|
59
|
+
|
|
60
|
+
function setupShellIfNeeded() {
|
|
61
|
+
if (mode !== 'shell') return;
|
|
62
|
+
const loader = ui.loader ? qs(root, ui.loader) : null;
|
|
63
|
+
const container = ui.container ? qs(root, ui.container) : null;
|
|
64
|
+
if (!container || !shell.iframeSrc) return;
|
|
65
|
+
|
|
66
|
+
const iframe = document.createElement('iframe');
|
|
67
|
+
iframe.src = shell.iframeSrc;
|
|
68
|
+
iframe.style.width = '100%';
|
|
69
|
+
iframe.style.height = '100%';
|
|
70
|
+
iframe.style.border = 'none';
|
|
71
|
+
iframe.style.display = 'none';
|
|
72
|
+
|
|
73
|
+
iframe.onload = function () {
|
|
74
|
+
iframe.style.display = 'block';
|
|
75
|
+
if (loader) {
|
|
76
|
+
loader.style.opacity = '0';
|
|
77
|
+
setTimeout(() => loader.style.display = 'none', 500);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
container.appendChild(iframe);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (elPrev) elPrev.addEventListener('click', () => { prev(); startAuto(); });
|
|
84
|
+
if (elNext) elNext.addEventListener('click', () => { next(); startAuto(); });
|
|
85
|
+
|
|
86
|
+
function start() {
|
|
87
|
+
idx = 0;
|
|
88
|
+
render();
|
|
89
|
+
startAuto();
|
|
90
|
+
if (mode === 'shell') setupShellIfNeeded();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return { start, stop: stopAuto, next, prev, hasCards };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Export global
|
|
97
|
+
global.initOnboarding = initOnboarding;
|
|
98
|
+
})(window);
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/* static/css/onboarding.css */
|
|
2
|
+
|
|
3
|
+
/* Fuente base para ambos contextos */
|
|
4
|
+
.ob-root {
|
|
5
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/* Tarjeta */
|
|
9
|
+
.ob-card {
|
|
10
|
+
background-color: #fff;
|
|
11
|
+
border-radius: 12px;
|
|
12
|
+
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
|
|
13
|
+
padding: 30px;
|
|
14
|
+
width: 90%;
|
|
15
|
+
max-width: 450px;
|
|
16
|
+
text-align: center;
|
|
17
|
+
transition: opacity 0.3s ease-in-out;
|
|
18
|
+
margin: 0 auto;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* Contenido de tarjeta */
|
|
22
|
+
.ob-icon {
|
|
23
|
+
font-size: 40px;
|
|
24
|
+
color: var(--brand-primary-color, #FF5100);
|
|
25
|
+
margin-bottom: 15px;
|
|
26
|
+
}
|
|
27
|
+
.ob-title {
|
|
28
|
+
font-size: 1.25rem;
|
|
29
|
+
color: #333;
|
|
30
|
+
margin-bottom: 10px;
|
|
31
|
+
}
|
|
32
|
+
.ob-text {
|
|
33
|
+
font-size: 0.95rem;
|
|
34
|
+
color: #666;
|
|
35
|
+
line-height: 1.5;
|
|
36
|
+
min-height: 60px;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* Navegación */
|
|
40
|
+
.ob-nav {
|
|
41
|
+
display: flex;
|
|
42
|
+
justify-content: space-between;
|
|
43
|
+
align-items: center;
|
|
44
|
+
margin-top: 20px;
|
|
45
|
+
}
|
|
46
|
+
.ob-btn {
|
|
47
|
+
background-color: var(--brand-secondary-color, #06326B);
|
|
48
|
+
border: none;
|
|
49
|
+
color: var(--brand-text-on-secondary, #FFFFFF);
|
|
50
|
+
border-radius: 50%;
|
|
51
|
+
width: 40px;
|
|
52
|
+
height: 40px;
|
|
53
|
+
cursor: pointer;
|
|
54
|
+
transition: opacity 0.2s;
|
|
55
|
+
display: inline-flex;
|
|
56
|
+
align-items: center;
|
|
57
|
+
justify-content: center;
|
|
58
|
+
}
|
|
59
|
+
.ob-btn:hover { opacity: 0.85; }
|
|
60
|
+
|
|
61
|
+
/* Dots */
|
|
62
|
+
.ob-dots {
|
|
63
|
+
display: flex;
|
|
64
|
+
gap: 8px;
|
|
65
|
+
}
|
|
66
|
+
.ob-dots > div {
|
|
67
|
+
width: 10px;
|
|
68
|
+
height: 10px;
|
|
69
|
+
border-radius: 50%;
|
|
70
|
+
background-color: #ddd;
|
|
71
|
+
transition: background-color 0.3s;
|
|
72
|
+
}
|
|
73
|
+
.ob-dots > div.active {
|
|
74
|
+
background-color: var(--brand-primary-color, #FF5100);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* Encabezado reutilizable (si se usa) */
|
|
78
|
+
.ob-brand-header {
|
|
79
|
+
font-size: 2rem;
|
|
80
|
+
font-weight: 700;
|
|
81
|
+
margin-bottom: 24px;
|
|
82
|
+
color: var(--brand-secondary-color, #06326B);
|
|
83
|
+
text-align: center;
|
|
84
|
+
}
|
|
85
|
+
.ob-brand-header .brand-name {
|
|
86
|
+
color: var(--brand-primary-color, #FF5100);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* Utilidades */
|
|
90
|
+
.ob-fade {
|
|
91
|
+
transition: opacity 0.5s ease-in-out;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* Responsivo */
|
|
95
|
+
@media (max-width: 420px) {
|
|
96
|
+
.ob-card { padding: 24px; }
|
|
97
|
+
.ob-icon { font-size: 34px; }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* Overlay a pantalla completa */
|
|
101
|
+
.onboarding-shell-root {
|
|
102
|
+
position: relative;
|
|
103
|
+
height: calc(100vh - 0px);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* Centrado vertical y horizontal del contenido del loader */
|
|
107
|
+
#loader-wrapper {
|
|
108
|
+
position: absolute; inset: 0;
|
|
109
|
+
background-color: #f4f7f6;
|
|
110
|
+
z-index: 1000;
|
|
111
|
+
display: flex; align-items: center; justify-content: center;
|
|
112
|
+
padding: 20px; box-sizing: border-box;
|
|
113
|
+
transition: opacity 0.5s ease-in-out;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* Pila vertical: header + tarjeta + banda de carga */
|
|
117
|
+
.ob-stack {
|
|
118
|
+
width: 100%;
|
|
119
|
+
max-width: 520px; /* ligeramente más que la tarjeta para respiración */
|
|
120
|
+
display: flex;
|
|
121
|
+
flex-direction: column;
|
|
122
|
+
align-items: stretch; /* la tarjeta ocupa el ancho */
|
|
123
|
+
gap: 16px;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/* Header de marca consistente */
|
|
127
|
+
.ob-brand-header {
|
|
128
|
+
font-size: 2rem;
|
|
129
|
+
font-weight: 700;
|
|
130
|
+
margin: 0;
|
|
131
|
+
color: var(--brand-secondary-color, #06326B);
|
|
132
|
+
text-align: center;
|
|
133
|
+
}
|
|
134
|
+
.ob-brand-header .brand-name {
|
|
135
|
+
color: var(--brand-primary-color, #FF5100);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* Banda de estado integrada visualmente con la tarjeta */
|
|
139
|
+
.ob-loading-band {
|
|
140
|
+
display: flex;
|
|
141
|
+
align-items: center;
|
|
142
|
+
justify-content: center;
|
|
143
|
+
gap: 12px;
|
|
144
|
+
padding: 12px 14px;
|
|
145
|
+
background: #ffffff;
|
|
146
|
+
border-radius: 12px;
|
|
147
|
+
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/* Spinner */
|
|
151
|
+
.spinner {
|
|
152
|
+
width: 28px; height: 28px;
|
|
153
|
+
border: 4px solid rgba(0,0,0,0.1);
|
|
154
|
+
border-top-color: var(--brand-primary-color, #FF5100);
|
|
155
|
+
border-radius: 50%;
|
|
156
|
+
animation: spin 1s linear infinite;
|
|
157
|
+
}
|
|
158
|
+
@keyframes spin { to { transform: rotate(360deg); } }
|
|
159
|
+
|
|
160
|
+
#loading-status p {
|
|
161
|
+
font-size: 0.95rem;
|
|
162
|
+
font-weight: 500;
|
|
163
|
+
color: #555;
|
|
164
|
+
margin: 0;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* Iframe contenedor */
|
|
168
|
+
#content-container { width: 100%; height: 100%; }
|
|
169
|
+
#content-container iframe { width: 100%; height: 100%; border: none; }
|
iatoolkit/templates/base.html
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/filepond/dist/filepond.min.css">
|
|
11
11
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css">
|
|
12
12
|
<link rel="stylesheet" href="{{ url_for('static', filename='styles/chat_iatoolkit.css', _external=True) }}">
|
|
13
|
+
<link rel="stylesheet" href="{{ url_for('static', filename='styles/onboarding.css', _external=True) }}">
|
|
13
14
|
<link rel="stylesheet" href="{{ url_for('static', filename='styles/chat_modal.css', _external=True) }}">
|
|
14
15
|
<link rel="stylesheet" href="{{ url_for('static', filename='styles/llm_output.css', _external=True) }}">
|
|
15
16
|
</head>
|
iatoolkit/templates/chat.html
CHANGED
|
@@ -4,9 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
{% block content %}
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
{
|
|
9
|
-
|
|
7
|
+
{% block styles %}
|
|
8
|
+
{# Movemos los estilos y los links aquí para que se rendericen en el <head> #}
|
|
9
|
+
<style>
|
|
10
|
+
{{ branding.css_variables | safe }}
|
|
11
|
+
</style>
|
|
12
|
+
<link rel="stylesheet" href="{{ url_for('static', filename='styles/onboarding.css', _external=True) }}">
|
|
13
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
|
|
14
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
|
15
|
+
{% endblock %}
|
|
10
16
|
|
|
11
17
|
<!-- Sección de encabezado con el usuario conectado -->
|
|
12
18
|
<div id="company-section" class="company-section d-flex justify-content-between align-items-center px-3 py-2"
|
|
@@ -49,14 +55,17 @@
|
|
|
49
55
|
class="ms-3 action-icon-style" title="Tu feedback es muy importante" style="color: {{ branding.header_text_color }};">
|
|
50
56
|
<i class="bi bi-emoji-smile"></i>
|
|
51
57
|
</a>
|
|
58
|
+
<a href="javascript:void(0);" id="onboarding-button"
|
|
59
|
+
class="ms-3 action-icon-style" title="Ver onboarding"
|
|
60
|
+
style="color: {{ branding.header_text_color }};">
|
|
61
|
+
<i class="bi bi-lightbulb"></i>
|
|
62
|
+
</a>
|
|
52
63
|
|
|
53
64
|
<!-- Icono de cerrar sesión (al final) -->
|
|
54
|
-
{
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
</a>
|
|
59
|
-
{% endif %}
|
|
65
|
+
<a href="{{ url_for('logout', company_short_name=company_short_name, _external=True) }}"
|
|
66
|
+
class="ms-3 action-icon-style" title="Cerrar sesión" style="color: {{ branding.header_text_color }} !important;">
|
|
67
|
+
<i class="bi bi-box-arrow-right"></i>
|
|
68
|
+
</a>
|
|
60
69
|
</div>
|
|
61
70
|
|
|
62
71
|
</div>
|
|
@@ -174,20 +183,16 @@
|
|
|
174
183
|
window.iatoolkit_base_url = "{{ iatoolkit_base_url }}";
|
|
175
184
|
window.availablePrompts = {{ prompts.message | tojson }};
|
|
176
185
|
window.sendButtonColor = "{{ branding.send_button_color }}";
|
|
177
|
-
|
|
178
|
-
{% if auth_method == 'jwt' and session_jwt %}
|
|
179
|
-
// Store session JWT if it exists, defined in the same global scope
|
|
180
|
-
window.sessionJWT = "{{ session_jwt }}";
|
|
181
|
-
{% endif %}
|
|
186
|
+
window.onboardingCards = {{ onboarding_cards | tojson }};
|
|
182
187
|
</script>
|
|
183
188
|
|
|
184
189
|
<!-- Carga de los scripts JS externos después de definir las variables globales -->
|
|
190
|
+
<script src="{{ url_for('static', filename='js/chat_onboarding.js', _external=True) }}"></script>
|
|
185
191
|
<script src="{{ url_for('static', filename='js/chat_filepond.js', _external=True) }}"></script>
|
|
186
192
|
<script src="{{ url_for('static', filename='js/chat_history.js', _external=True) }}"></script>
|
|
187
193
|
<script src="{{ url_for('static', filename='js/chat_feedback.js', _external=True) }}"></script>
|
|
188
194
|
<script src="{{ url_for('static', filename='js/chat_main.js', _external=True) }}"></script>
|
|
189
195
|
|
|
190
|
-
|
|
191
196
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css">
|
|
192
197
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
|
193
198
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"></script>
|
|
@@ -274,5 +279,44 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
274
279
|
});
|
|
275
280
|
});
|
|
276
281
|
});
|
|
282
|
+
|
|
283
|
+
// Inicialización del modal de onboarding
|
|
284
|
+
document.addEventListener('DOMContentLoaded', function () {
|
|
285
|
+
const btn = document.getElementById('onboarding-button');
|
|
286
|
+
if (!btn) return;
|
|
287
|
+
|
|
288
|
+
const modalEl = document.getElementById('onboardingModal');
|
|
289
|
+
const modal = new bootstrap.Modal(modalEl);
|
|
290
|
+
|
|
291
|
+
if (!window.initOnboarding) {
|
|
292
|
+
console.error('initOnboarding no disponible. Verifica chat_onboarding.js');
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const onboarding = initOnboarding({
|
|
297
|
+
mode: 'modal',
|
|
298
|
+
cards: Array.isArray(window.onboardingCards) ? window.onboardingCards : [],
|
|
299
|
+
ui: {
|
|
300
|
+
icon: '#ob-icon',
|
|
301
|
+
title: '#ob-title',
|
|
302
|
+
text: '#ob-text',
|
|
303
|
+
dots: '#ob-dots',
|
|
304
|
+
prev: '#ob-prev',
|
|
305
|
+
next: '#ob-next'
|
|
306
|
+
},
|
|
307
|
+
autoRotateMs: 5000
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
modalEl.addEventListener('hidden.bs.modal', () => onboarding.stop());
|
|
311
|
+
|
|
312
|
+
btn.addEventListener('click', () => {
|
|
313
|
+
if (!onboarding.hasCards()) {
|
|
314
|
+
toastr.info('No hay información de onboarding disponible.');
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
onboarding.start();
|
|
318
|
+
modal.show();
|
|
319
|
+
});
|
|
320
|
+
});
|
|
277
321
|
</script>
|
|
278
322
|
{% endblock %}
|
|
@@ -109,4 +109,37 @@
|
|
|
109
109
|
</div>
|
|
110
110
|
</div>
|
|
111
111
|
</div>
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
<!-- Modal de Onboarding -->
|
|
115
|
+
<div class="modal fade" id="onboardingModal" tabindex="-1" aria-labelledby="onboardingModalLabel" aria-hidden="true">
|
|
116
|
+
<div class="modal-dialog modal-dialog-centered">
|
|
117
|
+
<div class="modal-content" style="border-radius:12px;">
|
|
118
|
+
<div class="modal-header">
|
|
119
|
+
<h5 class="modal-title w-100 text-center" id="onboardingModalLabel" style="color: var(--brand-primary-color, #FF5100);">
|
|
120
|
+
Bienvenido a {{ branding.name }}
|
|
121
|
+
</h5>
|
|
122
|
+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cerrar"></button>
|
|
123
|
+
</div>
|
|
124
|
+
|
|
125
|
+
<div class="modal-body ob-root">
|
|
126
|
+
<div id="ob-card" class="ob-card">
|
|
127
|
+
<div id="ob-icon" class="ob-icon"><i class="bi bi-lightbulb"></i></div>
|
|
128
|
+
<h6 id="ob-title" class="ob-title"></h6>
|
|
129
|
+
<div id="ob-text" class="ob-text"></div>
|
|
130
|
+
<div class="ob-nav">
|
|
131
|
+
<button id="ob-prev" class="ob-btn" aria-label="Anterior"><i class="bi bi-chevron-left"></i></button>
|
|
132
|
+
<div id="ob-dots" class="ob-dots"></div>
|
|
133
|
+
<button id="ob-next" class="ob-btn" aria-label="Siguiente"><i class="bi bi-chevron-right"></i></button>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
<div class="modal-footer">
|
|
139
|
+
<button id="ob-close" type="button" class="btn btn-branded-primary" data-bs-dismiss="modal">
|
|
140
|
+
Cerrar
|
|
141
|
+
</button>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
112
145
|
</div>
|
|
@@ -1,150 +1,118 @@
|
|
|
1
1
|
{% extends "base.html" %}
|
|
2
2
|
|
|
3
|
-
{% block title %}
|
|
3
|
+
{% block title %}Prueba de Login para {{ branding.name }}{% endblock %}
|
|
4
|
+
|
|
5
|
+
{% block styles %}
|
|
6
|
+
{# Este bloque asegura que los colores de la marca estén disponibles como variables CSS #}
|
|
7
|
+
{% if branding and branding.css_variables %}
|
|
8
|
+
<style>
|
|
9
|
+
{{ branding.css_variables|safe }}
|
|
10
|
+
/* Usa la variable de CSS para el color primario del botón */
|
|
11
|
+
#initiateJwtChatButton {
|
|
12
|
+
background-color: var(--brand-primary-color, #0d6efd);
|
|
13
|
+
border-color: var(--brand-primary-color, #0d6efd);
|
|
14
|
+
}
|
|
15
|
+
</style>
|
|
16
|
+
{% endif %}
|
|
17
|
+
{% endblock %}
|
|
4
18
|
|
|
5
19
|
{% block content %}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
Este formulario permite testear el acceso a IAToolkit desde un portal externo utilizando una api-key.
|
|
18
|
-
|
|
19
|
-
</p>
|
|
20
|
-
</div>
|
|
21
|
-
<form id="jwt-form" method="post">
|
|
22
|
-
<div class="mb-3">
|
|
23
|
-
<label for="company_name" class="form-label d-block">Empresa</label>
|
|
24
|
-
<select id="company_name" name="company_short_name" class="form-select" required>
|
|
25
|
-
<option value="" disabled selected>Selecciona una empresa</option>
|
|
26
|
-
{% for company in companies %}
|
|
27
|
-
{% if company.allow_jwt %}
|
|
28
|
-
<option value="{{ company.short_name }}"> {{ company.short_name }}
|
|
29
|
-
</option>
|
|
30
|
-
{% endif %}
|
|
31
|
-
{% endfor %}
|
|
32
|
-
</select>
|
|
20
|
+
<div class="container-fluid">
|
|
21
|
+
<div class="row flex-fill mt-5 justify-content-center">
|
|
22
|
+
<!-- login desde sistema externo -->
|
|
23
|
+
<div class="col-12 col-lg-6">
|
|
24
|
+
<div class="border rounded p-4 p-md-5 shadow-sm bg-light">
|
|
25
|
+
{# El título ahora muestra dinámicamente el nombre de la empresa #}
|
|
26
|
+
<h3 class="text-muted fw-semibold text-start mb-3">
|
|
27
|
+
Login Externo para <span style="color: var(--brand-primary-color, #0d6efd);">{{ branding.name }}</span>
|
|
28
|
+
</h3>
|
|
29
|
+
<div class="text-center mb-4">
|
|
30
|
+
<p class="text-muted widget-intro-text">
|
|
31
|
+
Este formulario permite testear el acceso a IAToolkit desde un portal externo utilizando una api-key. El external user ID es el nombre del usuario ya autentificado en algún portal interno de la empresa.
|
|
32
|
+
</p>
|
|
33
33
|
</div>
|
|
34
34
|
|
|
35
|
+
{# El 'action' y 'method' son manejados por JS, pero el id es crucial #}
|
|
36
|
+
<form id="jwt-form">
|
|
37
|
+
|
|
35
38
|
<div class="mb-3">
|
|
36
39
|
<label for="external_user_id" class="form-label d-block">External user ID</label>
|
|
37
40
|
<input type="text" id="external_user_id" name="external_user_id" class="form-control" required>
|
|
38
41
|
</div>
|
|
39
42
|
|
|
40
|
-
<button type="
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
<span class="spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span>
|
|
44
|
-
Iniciar Sesión
|
|
43
|
+
<button type="submit" id="initiateJwtChatButton" class="btn btn-primary">
|
|
44
|
+
<span class="spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span>
|
|
45
|
+
Iniciar Sesión
|
|
45
46
|
</button>
|
|
46
47
|
</form>
|
|
47
48
|
</div>
|
|
48
49
|
</div>
|
|
49
|
-
|
|
50
50
|
</div>
|
|
51
|
-
|
|
51
|
+
</div>
|
|
52
52
|
{% endblock %}
|
|
53
53
|
|
|
54
54
|
{% block scripts %}
|
|
55
|
-
|
|
56
55
|
<script>
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Actualizamos al cargar la página
|
|
83
|
-
updateLinksAndForm();
|
|
84
|
-
|
|
85
|
-
// Escuchamos el evento de cambio en el dropdown para actualizar dinámicamente
|
|
86
|
-
$('#company_short_name').on('change', function () {
|
|
87
|
-
updateLinksAndForm();
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
// Interceptamos el click en "Registrarse"
|
|
91
|
-
$('#signup-link').on('click', function (e) {
|
|
92
|
-
const selectedCompany = $('#company_short_name').val();
|
|
93
|
-
|
|
94
|
-
if (!selectedCompany || selectedCompany.trim() === '') {
|
|
95
|
-
e.preventDefault(); // evitar navegación al #
|
|
96
|
-
Swal.fire({
|
|
97
|
-
icon: 'warning',
|
|
98
|
-
title: 'Empresa no seleccionada',
|
|
99
|
-
text: 'Por favor, selecciona una empresa antes de registrarte.'
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
// Event listener para el botón de "Abrir Chat (JWT)"
|
|
105
|
-
$('#initiateJwtChatButton').on('click', function() {
|
|
106
|
-
|
|
107
|
-
const selectedCompany = $('#company_name').val();
|
|
108
|
-
const externalUserId = $('#external_user_id').val();
|
|
109
|
-
|
|
110
|
-
if (!selectedCompany || !externalUserId.trim()) {
|
|
111
|
-
Swal.fire({ icon: 'warning', title: 'Campos Requeridos', text: 'Por favor, selecciona una empresa e ingresa un ID de usuario.' });
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
if (!API_KEY || API_KEY.includes("defecto")) {
|
|
115
|
-
Swal.fire({ icon: 'error', title: 'Error de Configuración', text: 'La API Key de la aplicación no está disponible.' });
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
fetch(`/${selectedCompany}/external_login`, {
|
|
120
|
-
method: 'POST',
|
|
121
|
-
headers: {
|
|
56
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
57
|
+
const companyShortName = "{{ company_short_name }}";
|
|
58
|
+
const apiKey = "{{ api_key }}";
|
|
59
|
+
const loginForm = document.getElementById('jwt-form');
|
|
60
|
+
const userIdInput = document.getElementById('external_user_id');
|
|
61
|
+
const submitButton = document.getElementById('initiateJwtChatButton');
|
|
62
|
+
const spinner = submitButton.querySelector('.spinner-border');
|
|
63
|
+
|
|
64
|
+
loginForm.addEventListener('submit', function(event) {
|
|
65
|
+
event.preventDefault();
|
|
66
|
+
const userIdentifier = userIdInput.value.trim();
|
|
67
|
+
if (!userIdentifier) return;
|
|
68
|
+
|
|
69
|
+
// Deshabilitar botón y mostrar spinner
|
|
70
|
+
submitButton.disabled = true;
|
|
71
|
+
spinner.classList.remove('d-none');
|
|
72
|
+
|
|
73
|
+
const newWindow = window.open('', '_blank');
|
|
74
|
+
const apiUrl = `/${companyShortName}/external_login`;
|
|
75
|
+
|
|
76
|
+
fetch(apiUrl, {
|
|
77
|
+
method: 'POST',
|
|
78
|
+
headers: {
|
|
122
79
|
'Content-Type': 'application/json',
|
|
123
|
-
'Authorization': `Bearer ${
|
|
80
|
+
'Authorization': `Bearer ${apiKey}`
|
|
124
81
|
},
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
.
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
82
|
+
body: JSON.stringify({ external_user_id: userIdentifier })
|
|
83
|
+
})
|
|
84
|
+
.then(response => response.ok ? response.text() : response.text().then(text => { throw new Error(text) }))
|
|
85
|
+
.then(html => {
|
|
86
|
+
newWindow.document.documentElement.innerHTML = html;
|
|
87
|
+
// Buscamos todos los scripts en el HTML inyectado y los re-ejecutamos.
|
|
88
|
+
const scripts = newWindow.document.querySelectorAll('script');
|
|
89
|
+
scripts.forEach(oldScript => {
|
|
90
|
+
const newScript = newWindow.document.createElement('script');
|
|
91
|
+
|
|
92
|
+
// Copiamos los atributos (como 'src' para archivos externos)
|
|
93
|
+
Array.from(oldScript.attributes).forEach(attr => {
|
|
94
|
+
newScript.setAttribute(attr.name, attr.value);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Copiamos el contenido para scripts inline
|
|
98
|
+
newScript.appendChild(newWindow.document.createTextNode(oldScript.innerHTML));
|
|
99
|
+
|
|
100
|
+
// Reemplazamos el script viejo por el nuevo para que se ejecute.
|
|
101
|
+
oldScript.parentNode.replaceChild(newScript, oldScript);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
})
|
|
105
|
+
.catch(error => {
|
|
106
|
+
console.error("Falló la prueba de login externo:", error);
|
|
107
|
+
const errorMessage = error.message || "Error desconocido.";
|
|
108
|
+
newWindow.document.body.innerHTML = `<div style="padding: 20px; font-family: monospace;"><h2>Error</h2><pre>${errorMessage}</pre></div>`;
|
|
109
|
+
})
|
|
110
|
+
.finally(() => {
|
|
111
|
+
// Volver a habilitar el botón y ocultar el spinner
|
|
112
|
+
submitButton.disabled = false;
|
|
113
|
+
spinner.classList.add('d-none');
|
|
114
|
+
});
|
|
115
|
+
});
|
|
148
116
|
});
|
|
149
117
|
</script>
|
|
150
118
|
{% endblock %}
|
|
@@ -1,167 +1,105 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
#progress-dots { display: flex; gap: 8px; }
|
|
58
|
-
.dot { width: 10px; height: 10px; border-radius: 50%; background-color: #ddd; transition: background-color 0.3s; }
|
|
59
|
-
.dot.active { background-color: var(--brand-primary-color, #FF5100); }
|
|
60
|
-
|
|
61
|
-
/* --- ESTILOS MEJORADOS: SPINNER DE CARGA --- */
|
|
62
|
-
#loading-status { margin-top: 30px; display: flex; align-items: center; gap: 15px; }
|
|
63
|
-
.spinner {
|
|
64
|
-
width: 30px; height: 30px; border: 4px solid rgba(0, 0, 0, 0.1);
|
|
65
|
-
border-top-color: var(--brand-primary-color, #FF5100);
|
|
66
|
-
border-radius: 50%;
|
|
67
|
-
animation: spin 1s linear infinite;
|
|
68
|
-
}
|
|
69
|
-
#loading-status p { font-size: 1rem; font-weight: 500; color: #555; margin: 0; }
|
|
70
|
-
@keyframes spin { to { transform: rotate(360deg); } }
|
|
71
|
-
|
|
72
|
-
/* --- Iframe (Sin cambios) --- */
|
|
73
|
-
#content-container { width: 100%; height: 100%; }
|
|
74
|
-
iframe { width: 100%; height: 100%; border: none; }
|
|
75
|
-
</style>
|
|
76
|
-
</head>
|
|
77
|
-
<body>
|
|
78
|
-
<div id="loader-wrapper">
|
|
79
|
-
|
|
80
|
-
<h1 id="brand-header">
|
|
81
|
-
<span class="brand-name">{{ branding.name | default('IAToolkit') }}</span> <span>IA</span>
|
|
82
|
-
</h1>
|
|
83
|
-
|
|
84
|
-
<div id="card-container">
|
|
85
|
-
<div id="card-icon" class="icon"><i class="fas fa-lightbulb"></i></div>
|
|
86
|
-
<h3 id="card-title">Título de la Tarjeta</h3>
|
|
87
|
-
<p id="card-text">Descripción de la tarjeta de capacitación.</p>
|
|
88
|
-
<div class="card-nav">
|
|
89
|
-
<button id="prev-card" aria-label="Anterior"><i class="fas fa-chevron-left"></i></button>
|
|
90
|
-
<div id="progress-dots"></div>
|
|
91
|
-
<button id="next-card" aria-label="Siguiente"><i class="fas fa-chevron-right"></i></button>
|
|
92
|
-
</div>
|
|
93
|
-
</div>
|
|
94
|
-
|
|
95
|
-
<!-- MEJORADO: Texto de estado ahora junto al spinner -->
|
|
96
|
-
<div id="loading-status">
|
|
97
|
-
<div class="spinner"></div>
|
|
98
|
-
<p>Inicializando el contexto de {{ branding.name }} para la IA...</p>
|
|
1
|
+
{% extends "base.html" %}
|
|
2
|
+
|
|
3
|
+
{% block title %}Iniciando {{ branding.name | default('IAToolkit') }} IA...{% endblock %}
|
|
4
|
+
|
|
5
|
+
{% block styles %}
|
|
6
|
+
{% endblock %}
|
|
7
|
+
|
|
8
|
+
{% block content %}
|
|
9
|
+
|
|
10
|
+
<link rel="stylesheet" href="{{ url_for('static', filename='styles/llm_output.css', _external=True) }}?v=6">
|
|
11
|
+
<link rel="stylesheet" href="{{ url_for('static', filename='styles/onboarding.css', _external=True) }}?v=6">
|
|
12
|
+
|
|
13
|
+
<style>
|
|
14
|
+
{# 1. Definimos las variables de la marca PRIMERO #}
|
|
15
|
+
{% if branding and branding.css_variables %}
|
|
16
|
+
{{ branding.css_variables|safe }}
|
|
17
|
+
{% endif %}
|
|
18
|
+
|
|
19
|
+
{# 2. Ahora definimos las reglas que usan esas variables #}
|
|
20
|
+
.onboarding-shell-root { position: relative; height: 100vh; }
|
|
21
|
+
.onboarding-shell-root #loader-wrapper { position: absolute; inset: 0; background: #f4f7f6; z-index: 1000; display:flex; align-items:center; justify-content:center; padding:20px; box-sizing:border-box; transition: opacity .5s; }
|
|
22
|
+
.onboarding-shell-root .ob-stack { width:100%; max-width:520px; display:flex; flex-direction:column; gap:16px; }
|
|
23
|
+
.onboarding-shell-root .ob-loading-band { display:flex; align-items:center; justify-content:center; gap:12px; padding:12px 14px; background:#fff; border-radius:12px; box-shadow:0 4px 20px rgba(0,0,0,.08); }
|
|
24
|
+
.onboarding-shell-root .spinner { width:28px; height:28px; border:4px solid rgba(0,0,0,.1); border-top-color: var(--brand-primary-color, #FF5100); border-radius:50%; animation: spin 1s linear infinite; }
|
|
25
|
+
@keyframes spin { to { transform: rotate(360deg); } }
|
|
26
|
+
.onboarding-shell-root #content-container { width:100%; height:100%; }
|
|
27
|
+
.onboarding-shell-root #content-container iframe { width:100%; height:100%; border:none; }
|
|
28
|
+
|
|
29
|
+
/* Forzar colores de marca (sube especificidad y evita overrides) */
|
|
30
|
+
.onboarding-shell-root .ob-brand-header { color: var(--brand-secondary-color, #06326B) !important; }
|
|
31
|
+
.onboarding-shell-root .ob-brand-header .brand-name { color: var(--brand-primary-color, #FF5100) !important; }
|
|
32
|
+
.onboarding-shell-root .ob-icon { color: var(--brand-primary-color, #FF5100) !important; }
|
|
33
|
+
.onboarding-shell-root .ob-btn { background-color: var(--brand-secondary-color, #06326B) !important; color: var(--brand-text-on-secondary, #FFFFFF) !important; border:none !important; }
|
|
34
|
+
.onboarding-shell-root .ob-dots > div.active { background-color: var(--brand-primary-color, #FF5100) !important; }
|
|
35
|
+
</style>
|
|
36
|
+
|
|
37
|
+
<div class="onboarding-shell-root ob-root">
|
|
38
|
+
<div id="loader-wrapper">
|
|
39
|
+
<div class="ob-stack">
|
|
40
|
+
<h1 id="ob-brand-header" class="ob-brand-header">
|
|
41
|
+
<span class="brand-name text-brand-primary">{{ branding.name | default('IAToolkit') }}</span>
|
|
42
|
+
<span class="brand-rest"> IA</span>
|
|
43
|
+
</h1>
|
|
44
|
+
|
|
45
|
+
<div id="card-container" class="ob-card">
|
|
46
|
+
<div id="card-icon" class="ob-icon"><i class="fas fa-lightbulb"></i></div>
|
|
47
|
+
<h3 id="card-title" class="ob-title">Título de la Tarjeta</h3>
|
|
48
|
+
<p id="card-text" class="ob-text">Descripción de la tarjeta de capacitación.</p>
|
|
49
|
+
<div class="ob-nav">
|
|
50
|
+
<button id="prev-card" class="ob-btn btn-branded-primary" aria-label="Anterior">
|
|
51
|
+
<i class="fas fa-chevron-left"></i>
|
|
52
|
+
</button>
|
|
53
|
+
<div id="progress-dots" class="ob-dots"></div>
|
|
54
|
+
<button id="next-card" class="ob-btn btn-branded-primary" aria-label="Siguiente">
|
|
55
|
+
<i class="fas fa-chevron-right"></i>
|
|
56
|
+
</button>
|
|
99
57
|
</div>
|
|
58
|
+
</div>
|
|
100
59
|
|
|
60
|
+
<div id="loading-status" class="ob-loading-band">
|
|
61
|
+
<div class="spinner"></div>
|
|
62
|
+
<p>Inicializando el contexto de {{ branding.name }} para la IA...</p>
|
|
63
|
+
</div>
|
|
101
64
|
</div>
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const $loader = $('#loader-wrapper');
|
|
138
|
-
const $container = $('#content-container');
|
|
139
|
-
|
|
140
|
-
// URL para el iframe, pasada desde la vista InitiateExternalChatView
|
|
141
|
-
const iframeSrc = "{{ iframe_src_url }}";
|
|
142
|
-
|
|
143
|
-
// Creamos el elemento iframe
|
|
144
|
-
const iframe = document.createElement('iframe');
|
|
145
|
-
iframe.src = iframeSrc;
|
|
146
|
-
|
|
147
|
-
// Estilos para que ocupe toda la pantalla
|
|
148
|
-
iframe.style.width = '100%';
|
|
149
|
-
iframe.style.height = '100%';
|
|
150
|
-
iframe.style.border = 'none';
|
|
151
|
-
iframe.style.display = 'none'; // Empezamos oculto
|
|
152
|
-
|
|
153
|
-
// Evento que se dispara cuando el iframe ha terminado de cargar su contenido
|
|
154
|
-
iframe.onload = function() {
|
|
155
|
-
// Mostramos el iframe
|
|
156
|
-
iframe.style.display = 'block';
|
|
157
|
-
// Ocultamos la animación de carga con una transición suave
|
|
158
|
-
$loader.css('opacity', 0);
|
|
159
|
-
setTimeout(() => $loader.hide(), 500); // Lo eliminamos del DOM después de la transición
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
// Añadimos el iframe al contenedor en el DOM
|
|
163
|
-
$container.append(iframe);
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<div id="content-container"></div>
|
|
68
|
+
</div>
|
|
69
|
+
{% endblock %}
|
|
70
|
+
|
|
71
|
+
{% block scripts %}
|
|
72
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
|
|
73
|
+
|
|
74
|
+
<script src="{{ url_for('static', filename='js/chat_onboarding.js', _external=True) }}"></script>
|
|
75
|
+
<script>
|
|
76
|
+
(function() {
|
|
77
|
+
const cardsData = {{ onboarding_cards | tojson }};
|
|
78
|
+
const iframeSrc = "{{ iframe_src_url }}";
|
|
79
|
+
|
|
80
|
+
if (!window.initOnboarding) {
|
|
81
|
+
console.error('initOnboarding no está disponible. Verifica la carga de chat_onboarding.js');
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const onboarding = initOnboarding({
|
|
86
|
+
mode: 'shell',
|
|
87
|
+
cards: cardsData,
|
|
88
|
+
ui: {
|
|
89
|
+
icon: '#card-icon',
|
|
90
|
+
title: '#card-title',
|
|
91
|
+
text: '#card-text',
|
|
92
|
+
dots: '#progress-dots',
|
|
93
|
+
prev: '#prev-card',
|
|
94
|
+
next: '#next-card',
|
|
95
|
+
loader: '#loader-wrapper',
|
|
96
|
+
container: '#content-container'
|
|
97
|
+
},
|
|
98
|
+
autoRotateMs: 5000,
|
|
99
|
+
shell: { iframeSrc: iframeSrc }
|
|
164
100
|
});
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
101
|
+
|
|
102
|
+
onboarding.start();
|
|
103
|
+
})();
|
|
104
|
+
</script>
|
|
105
|
+
{% endblock %}
|
|
@@ -36,15 +36,18 @@ class BaseLoginView(MethodView):
|
|
|
36
36
|
"""
|
|
37
37
|
Centralized logic to decide between the fast path and the slow path.
|
|
38
38
|
"""
|
|
39
|
+
# --- Get the company branding ---
|
|
40
|
+
branding_data = self.branding_service.get_company_branding(company)
|
|
41
|
+
|
|
42
|
+
# this service decides is the context needs to be rebuilt or not
|
|
39
43
|
prep_result = self.query_service.prepare_context(
|
|
40
44
|
company_short_name=company_short_name, user_identifier=user_identifier
|
|
41
45
|
)
|
|
42
|
-
|
|
43
|
-
branding_data = self.branding_service.get_company_branding(company)
|
|
44
|
-
|
|
45
46
|
if prep_result.get('rebuild_needed'):
|
|
46
47
|
# --- SLOW PATH: Render the loading shell ---
|
|
47
48
|
onboarding_cards = self.onboarding_service.get_onboarding_cards(company)
|
|
49
|
+
|
|
50
|
+
# callback url to call when the context finish loading
|
|
48
51
|
target_url = url_for('finalize_context_load', company_short_name=company_short_name, _external=True)
|
|
49
52
|
|
|
50
53
|
return render_template(
|
|
@@ -56,8 +59,10 @@ class BaseLoginView(MethodView):
|
|
|
56
59
|
else:
|
|
57
60
|
# --- FAST PATH: Render the chat page directly ---
|
|
58
61
|
prompts = self.prompt_service.get_user_prompts(company_short_name)
|
|
62
|
+
onboarding_cards = self.onboarding_service.get_onboarding_cards(company)
|
|
59
63
|
return render_template(
|
|
60
64
|
"chat.html",
|
|
61
65
|
branding=branding_data,
|
|
62
66
|
prompts=prompts,
|
|
67
|
+
onboarding_cards=onboarding_cards
|
|
63
68
|
)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
from flask.views import MethodView
|
|
5
|
+
from flask import Response, abort, request, make_response
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class LoginSimulationView(MethodView):
|
|
9
|
+
"""
|
|
10
|
+
Simula un portal externo que llama a la API de IAToolkit de servidor a servidor,
|
|
11
|
+
replicando el flujo real de `dispatch_request`.
|
|
12
|
+
Para usarlo, visita /login_test/<company_short_name>/<external_user_id>
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def get(self, company_short_name: str, external_user_id: str):
|
|
16
|
+
api_key = os.getenv("IATOOLKIT_API_KEY")
|
|
17
|
+
base_url = request.host_url.rstrip('/')
|
|
18
|
+
|
|
19
|
+
if not api_key:
|
|
20
|
+
abort(500, "Error: IATOOLKIT_API_KEY no está configurada en el servidor de prueba.")
|
|
21
|
+
if not external_user_id:
|
|
22
|
+
abort(400, "Error: Debes proporcionar un external_user_id en la URL.")
|
|
23
|
+
|
|
24
|
+
target_url = f"{base_url}/{company_short_name}/external_login"
|
|
25
|
+
|
|
26
|
+
# --- INICIO DE LA CORRECCIÓN ---
|
|
27
|
+
# Usamos el formato de header 'Authorization: Bearer' como solicitaste.
|
|
28
|
+
headers = {
|
|
29
|
+
'Content-Type': 'application/json',
|
|
30
|
+
'Authorization': f'Bearer {api_key}'
|
|
31
|
+
}
|
|
32
|
+
# --- FIN DE LA CORRECCIÓN ---
|
|
33
|
+
|
|
34
|
+
payload = {'external_user_id': external_user_id}
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
# Llamada POST interna. 'stream=True' es importante para manejar la respuesta.
|
|
38
|
+
internal_response = requests.post(target_url, headers=headers, data=json.dumps(payload), timeout=120,
|
|
39
|
+
stream=True)
|
|
40
|
+
internal_response.raise_for_status()
|
|
41
|
+
|
|
42
|
+
# Creamos una nueva Response de Flask para el navegador del usuario.
|
|
43
|
+
user_response = Response(
|
|
44
|
+
internal_response.iter_content(chunk_size=1024),
|
|
45
|
+
status=internal_response.status_code
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# Copiamos TODAS las cabeceras de la respuesta interna a la respuesta final,
|
|
49
|
+
# incluyendo 'Content-Type' y, crucialmente, 'Set-Cookie'.
|
|
50
|
+
for key, value in internal_response.headers.items():
|
|
51
|
+
if key.lower() not in ['content-encoding', 'content-length', 'transfer-encoding', 'connection']:
|
|
52
|
+
user_response.headers[key] = value
|
|
53
|
+
|
|
54
|
+
return user_response
|
|
55
|
+
|
|
56
|
+
except requests.exceptions.HTTPError as e:
|
|
57
|
+
error_text = f"Error en la llamada interna a la API: {e.response.status_code}. Respuesta: {e.response.text}"
|
|
58
|
+
return Response(error_text, status=e.response.status_code, mimetype='text/plain')
|
|
59
|
+
except requests.exceptions.RequestException as e:
|
|
60
|
+
return Response(f'Error de conexión con el servicio de IA: {str(e)}', status=502, mimetype='text/plain')
|
iatoolkit/views/login_view.py
CHANGED
|
@@ -10,6 +10,7 @@ from iatoolkit.services.profile_service import ProfileService
|
|
|
10
10
|
from iatoolkit.services.query_service import QueryService
|
|
11
11
|
from iatoolkit.services.prompt_manager_service import PromptService
|
|
12
12
|
from iatoolkit.services.branding_service import BrandingService
|
|
13
|
+
from iatoolkit.services.onboarding_service import OnboardingService
|
|
13
14
|
from iatoolkit.views.base_login_view import BaseLoginView
|
|
14
15
|
|
|
15
16
|
|
|
@@ -67,12 +68,14 @@ class FinalizeContextView(MethodView):
|
|
|
67
68
|
profile_service: ProfileService,
|
|
68
69
|
query_service: QueryService,
|
|
69
70
|
prompt_service: PromptService,
|
|
70
|
-
branding_service: BrandingService
|
|
71
|
+
branding_service: BrandingService,
|
|
72
|
+
onboarding_service: OnboardingService
|
|
71
73
|
):
|
|
72
74
|
self.profile_service = profile_service
|
|
73
75
|
self.query_service = query_service
|
|
74
76
|
self.prompt_service = prompt_service
|
|
75
77
|
self.branding_service = branding_service
|
|
78
|
+
self.onboarding_service = onboarding_service
|
|
76
79
|
|
|
77
80
|
def get(self, company_short_name: str):
|
|
78
81
|
# 1. Use the centralized method to get session info.
|
|
@@ -81,7 +84,7 @@ class FinalizeContextView(MethodView):
|
|
|
81
84
|
|
|
82
85
|
if not user_identifier:
|
|
83
86
|
# This can happen if the session expires or is invalid.
|
|
84
|
-
return redirect(url_for('
|
|
87
|
+
return redirect(url_for('index', company_short_name=company_short_name))
|
|
85
88
|
|
|
86
89
|
company = self.profile_service.get_company_by_short_name(company_short_name)
|
|
87
90
|
if not company:
|
|
@@ -97,11 +100,13 @@ class FinalizeContextView(MethodView):
|
|
|
97
100
|
# 3. render the chat page.
|
|
98
101
|
prompts = self.prompt_service.get_user_prompts(company_short_name)
|
|
99
102
|
branding_data = self.branding_service.get_company_branding(company)
|
|
103
|
+
onboarding_cards = self.onboarding_service.get_onboarding_cards(company)
|
|
100
104
|
|
|
101
105
|
return render_template(
|
|
102
106
|
"chat.html",
|
|
103
107
|
branding=branding_data,
|
|
104
108
|
prompts=prompts,
|
|
109
|
+
onboarding_cards=onboarding_cards
|
|
105
110
|
)
|
|
106
111
|
|
|
107
112
|
except Exception as e:
|
|
@@ -2,10 +2,10 @@ iatoolkit/__init__.py,sha256=4PWjMJjktixtrxF6BY405qyA50Sv967kEP2x-oil6qk,1120
|
|
|
2
2
|
iatoolkit/base_company.py,sha256=uFJmy77LPAceVqkTeuJqo15-auDiq4aTwvC_bbBD0mQ,4607
|
|
3
3
|
iatoolkit/cli_commands.py,sha256=G5L9xQXZ0lVFXQWBaE_KEZHyfuiT6PL1nTQRoSdnBzc,2302
|
|
4
4
|
iatoolkit/company_registry.py,sha256=tduqt3oV8iDX_IB1eA7KIgvIxE4edTcy-3qZIXh3Lzw,2549
|
|
5
|
-
iatoolkit/iatoolkit.py,sha256=
|
|
5
|
+
iatoolkit/iatoolkit.py,sha256=HxmkgpwobTvw7jiDGhKh6yJFJC3o6ZgfW4muirO51W8,17305
|
|
6
6
|
iatoolkit/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
7
|
iatoolkit/common/exceptions.py,sha256=EXx40n5htp7UiOM6P1xfJ9U6NMcADqm62dlFaKz7ICU,1154
|
|
8
|
-
iatoolkit/common/routes.py,sha256=
|
|
8
|
+
iatoolkit/common/routes.py,sha256=LniD_xsVZ6iHx1Z2_daOChhXZ9AQwsYFtqWSuFkQVyk,6117
|
|
9
9
|
iatoolkit/common/session_manager.py,sha256=UeKfD15bcEA3P5e0WSURfotLqpsiIMp3AXxAMhtgHs0,471
|
|
10
10
|
iatoolkit/common/util.py,sha256=w9dTd3csK0gKtFSp-a4t7XmCPZiYDhiON92uXRbTT8A,14624
|
|
11
11
|
iatoolkit/infra/__init__.py,sha256=5JqK9sZ6jBuK83zDQokUhxQ0wuJJJ9DXB8pYCLkX7X4,102
|
|
@@ -67,11 +67,13 @@ iatoolkit/static/js/chat_feedback.js,sha256=DDT2NPgglrLnW75vtEAVXS72MNt7vlMcavzr
|
|
|
67
67
|
iatoolkit/static/js/chat_filepond.js,sha256=mzXafm7a506EpM37KATTK3zvAswO1E0KSUY1vKbwuRc,3163
|
|
68
68
|
iatoolkit/static/js/chat_history.js,sha256=4hYNODIwYNd5vaQqkR28HZyXYIFKgSayrnmOuT_DUac,4381
|
|
69
69
|
iatoolkit/static/js/chat_main.js,sha256=j3rbJjWWCEAM1XUXPv6K2SW4S3kBrPAwEzVtLTjHslI,17314
|
|
70
|
-
iatoolkit/static/
|
|
70
|
+
iatoolkit/static/js/chat_onboarding.js,sha256=b6ofiFcPhuCaPmSFIvDQZqcMUVvbI7LpIsjZOZJUSAU,3185
|
|
71
|
+
iatoolkit/static/styles/chat_iatoolkit.css,sha256=aA-PZ2TGl_k82JSVVBC2-CJT0NiZAuLOGoiaJhdeVUU,11416
|
|
71
72
|
iatoolkit/static/styles/chat_info.css,sha256=17DbgoNYE21VYWfb5L9-QLCpD2R1idK4imKRLwXtJLY,1058
|
|
72
73
|
iatoolkit/static/styles/chat_modal.css,sha256=mdfjrJtmUn3O9rKwIGjJc-oSNmJGnzUY1aAJqEfPh38,4301
|
|
73
74
|
iatoolkit/static/styles/landing_page.css,sha256=5MHlXkmgZVv9uHE7rZTGYzZeynya3ONY4hp7e2uPXwk,4898
|
|
74
75
|
iatoolkit/static/styles/llm_output.css,sha256=AlxgRSOleeCk2dLAqFWVaQ-jwZiJjcpC5rHuUv3T6VU,2312
|
|
76
|
+
iatoolkit/static/styles/onboarding.css,sha256=Bo0hd8ngVy404_a-gtNFi-hzljhIAnpE-1oQJGnj0F0,3655
|
|
75
77
|
iatoolkit/system_prompts/format_styles.prompt,sha256=MSMe1qvR3cF_0IbFshn8R0z6Wx6VCHQq1p37rpu5wwk,3576
|
|
76
78
|
iatoolkit/system_prompts/query_main.prompt,sha256=D2Wjf0uunQIQsQiJVrY-BTQz6PemM5En6ftmw_c5t4E,2808
|
|
77
79
|
iatoolkit/system_prompts/sql_rules.prompt,sha256=y4nURVnb9AyFwt-lrbMNBHHtZlhk6kC9grYoOhRnrJo,59174
|
|
@@ -79,20 +81,20 @@ iatoolkit/templates/_branding_styles.html,sha256=x0GJmY1WWpPxKBUoqmxh685_1c6-4uL
|
|
|
79
81
|
iatoolkit/templates/_login_widget.html,sha256=p7Xz0P1Xd3Otn41uVQTA3GmDswMrUVMIn9doDNJojAA,1852
|
|
80
82
|
iatoolkit/templates/_navbar.html,sha256=o1PvZE5ueLmVpGUAmsjtu-vS_WPROTlJc2sTXl6AS4Y,360
|
|
81
83
|
iatoolkit/templates/about.html,sha256=ciC08grUVz5qLzdzDDqDX31xirg5PrJIRYabWpV9oA8,294
|
|
82
|
-
iatoolkit/templates/base.html,sha256=
|
|
84
|
+
iatoolkit/templates/base.html,sha256=hHfBqZJsPcZGlb4BxAbHvpJFcSjckLIAVTYTmoyXrz0,2323
|
|
83
85
|
iatoolkit/templates/change_password.html,sha256=G5a3hYLTpz_5Q_eZ4LNcYSqLeW-CuT4NCHD8bkhAd9k,3573
|
|
84
|
-
iatoolkit/templates/chat.html,sha256=
|
|
85
|
-
iatoolkit/templates/chat_modals.html,sha256=
|
|
86
|
+
iatoolkit/templates/chat.html,sha256=AgVynrJzUlBr8aG334YKg3U35Nas0-RoVfzFAx9Ntfk,13961
|
|
87
|
+
iatoolkit/templates/chat_modals.html,sha256=NwwgPoOmVbjy4aO2eHsy1TUMXRiOfTOC5Jx_F2ehhcs,6947
|
|
86
88
|
iatoolkit/templates/error.html,sha256=c3dxieMygsvdjQMiQu_sn6kqqag9zFtVu-z5FunX6so,580
|
|
87
89
|
iatoolkit/templates/forgot_password.html,sha256=NRZqbNHJXSLNArF_KLbzuem-U57v07awS0ikI_DJbfM,2360
|
|
88
90
|
iatoolkit/templates/header.html,sha256=179agI7rnYwP_rvJNXIiVde5E8Ec5649_XKq6eew2Hk,1263
|
|
89
91
|
iatoolkit/templates/index.html,sha256=jBLCqZoIMmUCA1KQESW9BcEgaxE0Rjs-4-i2V0ueSMo,7654
|
|
90
|
-
iatoolkit/templates/login_test.html,sha256=
|
|
91
|
-
iatoolkit/templates/onboarding_shell.html,sha256=
|
|
92
|
+
iatoolkit/templates/login_test.html,sha256=ApvW4l0UmcjJ9pBl3jm-0BojWwiFV6ZyCWz3caM1DN4,4722
|
|
93
|
+
iatoolkit/templates/onboarding_shell.html,sha256=r1ivSR2ci8GrDSm1uaD-cf78rfO1bKT5gXa-v5aHLAk,4659
|
|
92
94
|
iatoolkit/templates/signup.html,sha256=9ArDvcNQgHFR2dwxy-37AXzGUOeOsT7Nz5u0y6fAB3U,4385
|
|
93
95
|
iatoolkit/templates/test.html,sha256=rwNtxC83tbCl5COZFXYvmRBxxmgFJtPNuVBd_nq9KWY,133
|
|
94
96
|
iatoolkit/views/__init__.py,sha256=5JqK9sZ6jBuK83zDQokUhxQ0wuJJJ9DXB8pYCLkX7X4,102
|
|
95
|
-
iatoolkit/views/base_login_view.py,sha256=
|
|
97
|
+
iatoolkit/views/base_login_view.py,sha256=6IWJXEg_6K9cPYoKnG_-Kpt_hIdYnv5ZNC5anIrytXk,2819
|
|
96
98
|
iatoolkit/views/change_password_view.py,sha256=tM0woZyKdhY4XYjS_YXg2sKq3RYkXGfcq_eVAKrNvNM,4498
|
|
97
99
|
iatoolkit/views/chat_token_request_view.py,sha256=wf32_A2Sq8NHYWshCwL10Tovd1znLoD0jQjzutR3sVE,4408
|
|
98
100
|
iatoolkit/views/external_login_view.py,sha256=qY5wxh98HyKI0Q1Phf6pkOzRGD7PvbH27GUZn0ccYu4,2889
|
|
@@ -103,15 +105,15 @@ iatoolkit/views/index_view.py,sha256=P5aVdEWxsYOZGbzcXd6WFE733qZ7YXIoeqriUMAM6V8
|
|
|
103
105
|
iatoolkit/views/init_context_api_view.py,sha256=1j8NKfODfPrffbA5YO8TPMHh-ildlLNzReIxv_qO-W4,2586
|
|
104
106
|
iatoolkit/views/llmquery_api_view.py,sha256=Rh-y-VENwwtNsDrYAD_SWKwjK16fW-pFRWlEvI-OYwY,2120
|
|
105
107
|
iatoolkit/views/llmquery_web_view.py,sha256=WhjlA1mfsoL8hL9tlKQfjCUcaTzT43odlp_uQKmT314,1500
|
|
106
|
-
iatoolkit/views/
|
|
107
|
-
iatoolkit/views/login_view.py,sha256=
|
|
108
|
+
iatoolkit/views/login_simulation_view.py,sha256=hujduAuq84YyHXfG1iVVbypkwYOreKr9FnQNvIXBDyQ,2675
|
|
109
|
+
iatoolkit/views/login_view.py,sha256=3mV-I1KWrVCi4GBmSWCPmIIKj0reZEBMmyTZdBKFCFM,4630
|
|
108
110
|
iatoolkit/views/prompt_api_view.py,sha256=MP0r-MiswwKcbNc_5KY7aVbHkrR218I8XCiCX1D0yTA,1244
|
|
109
111
|
iatoolkit/views/signup_view.py,sha256=BCjhM2lMiDPwYrlW_eEwPl-ZLupblbFfsonWtq0E4vU,3922
|
|
110
112
|
iatoolkit/views/tasks_review_view.py,sha256=keLsLCyOTTlcoIapnB_lbuSvLwrPVZVpBiFC_7ChbLg,3388
|
|
111
113
|
iatoolkit/views/tasks_view.py,sha256=a3anTXrJTTvbQuc6PSpOzidLKQFL4hWa7PI2Cppcz8w,4110
|
|
112
114
|
iatoolkit/views/user_feedback_api_view.py,sha256=59XB9uQLHI4Q6QA4_XhK787HzfXb-c6EY7k1Ccyr4hI,2424
|
|
113
115
|
iatoolkit/views/verify_user_view.py,sha256=7XLSaxvs8LjBr3cYOUDa9B8DqW_50IGlq0IvmOQcD0Y,2340
|
|
114
|
-
iatoolkit-0.
|
|
115
|
-
iatoolkit-0.
|
|
116
|
-
iatoolkit-0.
|
|
117
|
-
iatoolkit-0.
|
|
116
|
+
iatoolkit-0.55.1.dist-info/METADATA,sha256=lZ5zZ-aChnuT972ggAucr4472SkboG8DDnUWIn8CfzQ,9301
|
|
117
|
+
iatoolkit-0.55.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
118
|
+
iatoolkit-0.55.1.dist-info/top_level.txt,sha256=V_w4QvDx0b1RXiy8zTCrD1Bp7AZkFe3_O0-9fMiwogg,10
|
|
119
|
+
iatoolkit-0.55.1.dist-info/RECORD,,
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
-
# Product: IAToolkit
|
|
3
|
-
#
|
|
4
|
-
# IAToolkit is open source software.
|
|
5
|
-
|
|
6
|
-
from flask.views import MethodView
|
|
7
|
-
from flask import render_template, request
|
|
8
|
-
from injector import inject
|
|
9
|
-
from iatoolkit.services.profile_service import ProfileService
|
|
10
|
-
import os
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class LoginTest(MethodView):
|
|
14
|
-
@inject
|
|
15
|
-
def __init__(self,
|
|
16
|
-
profile_service: ProfileService):
|
|
17
|
-
self.profile_service = profile_service
|
|
18
|
-
|
|
19
|
-
def get(self):
|
|
20
|
-
alert_message = request.args.get('alert_message', None)
|
|
21
|
-
companies = self.profile_service.get_companies()
|
|
22
|
-
|
|
23
|
-
# Esta API_KEY para el login
|
|
24
|
-
api_key_for_login = os.getenv("IATOOLKIT_API_KEY", "tu_api_key_por_defecto_o_error")
|
|
25
|
-
|
|
26
|
-
return render_template('login_test.html',
|
|
27
|
-
companies=companies,
|
|
28
|
-
alert_message=alert_message,
|
|
29
|
-
alert_icon='success' if alert_message else None,
|
|
30
|
-
api_key=api_key_for_login
|
|
31
|
-
)
|
|
File without changes
|
|
File without changes
|