iatoolkit 0.3.9__py3-none-any.whl → 0.107.4__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 (150) hide show
  1. iatoolkit/__init__.py +27 -35
  2. iatoolkit/base_company.py +3 -35
  3. iatoolkit/cli_commands.py +18 -47
  4. iatoolkit/common/__init__.py +0 -0
  5. iatoolkit/common/exceptions.py +48 -0
  6. iatoolkit/common/interfaces/__init__.py +0 -0
  7. iatoolkit/common/interfaces/asset_storage.py +34 -0
  8. iatoolkit/common/interfaces/database_provider.py +39 -0
  9. iatoolkit/common/model_registry.py +159 -0
  10. iatoolkit/common/routes.py +138 -0
  11. iatoolkit/common/session_manager.py +26 -0
  12. iatoolkit/common/util.py +353 -0
  13. iatoolkit/company_registry.py +66 -29
  14. iatoolkit/core.py +514 -0
  15. iatoolkit/infra/__init__.py +5 -0
  16. iatoolkit/infra/brevo_mail_app.py +123 -0
  17. iatoolkit/infra/call_service.py +140 -0
  18. iatoolkit/infra/connectors/__init__.py +5 -0
  19. iatoolkit/infra/connectors/file_connector.py +17 -0
  20. iatoolkit/infra/connectors/file_connector_factory.py +57 -0
  21. iatoolkit/infra/connectors/google_cloud_storage_connector.py +53 -0
  22. iatoolkit/infra/connectors/google_drive_connector.py +68 -0
  23. iatoolkit/infra/connectors/local_file_connector.py +46 -0
  24. iatoolkit/infra/connectors/s3_connector.py +33 -0
  25. iatoolkit/infra/google_chat_app.py +57 -0
  26. iatoolkit/infra/llm_providers/__init__.py +0 -0
  27. iatoolkit/infra/llm_providers/deepseek_adapter.py +278 -0
  28. iatoolkit/infra/llm_providers/gemini_adapter.py +350 -0
  29. iatoolkit/infra/llm_providers/openai_adapter.py +124 -0
  30. iatoolkit/infra/llm_proxy.py +268 -0
  31. iatoolkit/infra/llm_response.py +45 -0
  32. iatoolkit/infra/redis_session_manager.py +122 -0
  33. iatoolkit/locales/en.yaml +222 -0
  34. iatoolkit/locales/es.yaml +225 -0
  35. iatoolkit/repositories/__init__.py +5 -0
  36. iatoolkit/repositories/database_manager.py +187 -0
  37. iatoolkit/repositories/document_repo.py +33 -0
  38. iatoolkit/repositories/filesystem_asset_repository.py +36 -0
  39. iatoolkit/repositories/llm_query_repo.py +105 -0
  40. iatoolkit/repositories/models.py +279 -0
  41. iatoolkit/repositories/profile_repo.py +171 -0
  42. iatoolkit/repositories/vs_repo.py +150 -0
  43. iatoolkit/services/__init__.py +5 -0
  44. iatoolkit/services/auth_service.py +193 -0
  45. {services → iatoolkit/services}/benchmark_service.py +7 -7
  46. iatoolkit/services/branding_service.py +153 -0
  47. iatoolkit/services/company_context_service.py +214 -0
  48. iatoolkit/services/configuration_service.py +375 -0
  49. iatoolkit/services/dispatcher_service.py +134 -0
  50. {services → iatoolkit/services}/document_service.py +20 -8
  51. iatoolkit/services/embedding_service.py +148 -0
  52. iatoolkit/services/excel_service.py +156 -0
  53. {services → iatoolkit/services}/file_processor_service.py +36 -21
  54. iatoolkit/services/history_manager_service.py +208 -0
  55. iatoolkit/services/i18n_service.py +104 -0
  56. iatoolkit/services/jwt_service.py +80 -0
  57. iatoolkit/services/language_service.py +89 -0
  58. iatoolkit/services/license_service.py +82 -0
  59. iatoolkit/services/llm_client_service.py +438 -0
  60. iatoolkit/services/load_documents_service.py +174 -0
  61. iatoolkit/services/mail_service.py +213 -0
  62. {services → iatoolkit/services}/profile_service.py +200 -101
  63. iatoolkit/services/prompt_service.py +303 -0
  64. iatoolkit/services/query_service.py +467 -0
  65. iatoolkit/services/search_service.py +55 -0
  66. iatoolkit/services/sql_service.py +169 -0
  67. iatoolkit/services/tool_service.py +246 -0
  68. iatoolkit/services/user_feedback_service.py +117 -0
  69. iatoolkit/services/user_session_context_service.py +213 -0
  70. iatoolkit/static/images/fernando.jpeg +0 -0
  71. iatoolkit/static/images/iatoolkit_core.png +0 -0
  72. iatoolkit/static/images/iatoolkit_logo.png +0 -0
  73. iatoolkit/static/js/chat_feedback_button.js +80 -0
  74. iatoolkit/static/js/chat_filepond.js +85 -0
  75. iatoolkit/static/js/chat_help_content.js +124 -0
  76. iatoolkit/static/js/chat_history_button.js +110 -0
  77. iatoolkit/static/js/chat_logout_button.js +36 -0
  78. iatoolkit/static/js/chat_main.js +401 -0
  79. iatoolkit/static/js/chat_model_selector.js +227 -0
  80. iatoolkit/static/js/chat_onboarding_button.js +103 -0
  81. iatoolkit/static/js/chat_prompt_manager.js +94 -0
  82. iatoolkit/static/js/chat_reload_button.js +38 -0
  83. iatoolkit/static/styles/chat_iatoolkit.css +559 -0
  84. iatoolkit/static/styles/chat_modal.css +133 -0
  85. iatoolkit/static/styles/chat_public.css +135 -0
  86. iatoolkit/static/styles/documents.css +598 -0
  87. iatoolkit/static/styles/landing_page.css +398 -0
  88. iatoolkit/static/styles/llm_output.css +148 -0
  89. iatoolkit/static/styles/onboarding.css +176 -0
  90. iatoolkit/system_prompts/__init__.py +0 -0
  91. iatoolkit/system_prompts/query_main.prompt +30 -23
  92. iatoolkit/system_prompts/sql_rules.prompt +47 -12
  93. iatoolkit/templates/_company_header.html +45 -0
  94. iatoolkit/templates/_login_widget.html +42 -0
  95. iatoolkit/templates/base.html +78 -0
  96. iatoolkit/templates/change_password.html +66 -0
  97. iatoolkit/templates/chat.html +337 -0
  98. iatoolkit/templates/chat_modals.html +185 -0
  99. iatoolkit/templates/error.html +51 -0
  100. iatoolkit/templates/forgot_password.html +51 -0
  101. iatoolkit/templates/onboarding_shell.html +106 -0
  102. iatoolkit/templates/signup.html +79 -0
  103. iatoolkit/views/__init__.py +5 -0
  104. iatoolkit/views/base_login_view.py +96 -0
  105. iatoolkit/views/change_password_view.py +116 -0
  106. iatoolkit/views/chat_view.py +76 -0
  107. iatoolkit/views/embedding_api_view.py +65 -0
  108. iatoolkit/views/forgot_password_view.py +75 -0
  109. iatoolkit/views/help_content_api_view.py +54 -0
  110. iatoolkit/views/history_api_view.py +56 -0
  111. iatoolkit/views/home_view.py +63 -0
  112. iatoolkit/views/init_context_api_view.py +74 -0
  113. iatoolkit/views/llmquery_api_view.py +59 -0
  114. iatoolkit/views/load_company_configuration_api_view.py +49 -0
  115. iatoolkit/views/load_document_api_view.py +65 -0
  116. iatoolkit/views/login_view.py +170 -0
  117. iatoolkit/views/logout_api_view.py +57 -0
  118. iatoolkit/views/profile_api_view.py +46 -0
  119. iatoolkit/views/prompt_api_view.py +37 -0
  120. iatoolkit/views/root_redirect_view.py +22 -0
  121. iatoolkit/views/signup_view.py +100 -0
  122. iatoolkit/views/static_page_view.py +27 -0
  123. iatoolkit/views/user_feedback_api_view.py +60 -0
  124. iatoolkit/views/users_api_view.py +33 -0
  125. iatoolkit/views/verify_user_view.py +60 -0
  126. iatoolkit-0.107.4.dist-info/METADATA +268 -0
  127. iatoolkit-0.107.4.dist-info/RECORD +132 -0
  128. iatoolkit-0.107.4.dist-info/licenses/LICENSE +21 -0
  129. iatoolkit-0.107.4.dist-info/licenses/LICENSE_COMMUNITY.md +15 -0
  130. {iatoolkit-0.3.9.dist-info → iatoolkit-0.107.4.dist-info}/top_level.txt +0 -1
  131. iatoolkit/iatoolkit.py +0 -413
  132. iatoolkit/system_prompts/arquitectura.prompt +0 -32
  133. iatoolkit-0.3.9.dist-info/METADATA +0 -252
  134. iatoolkit-0.3.9.dist-info/RECORD +0 -32
  135. services/__init__.py +0 -5
  136. services/api_service.py +0 -75
  137. services/dispatcher_service.py +0 -351
  138. services/excel_service.py +0 -98
  139. services/history_service.py +0 -45
  140. services/jwt_service.py +0 -91
  141. services/load_documents_service.py +0 -212
  142. services/mail_service.py +0 -62
  143. services/prompt_manager_service.py +0 -172
  144. services/query_service.py +0 -334
  145. services/search_service.py +0 -32
  146. services/sql_service.py +0 -42
  147. services/tasks_service.py +0 -188
  148. services/user_feedback_service.py +0 -67
  149. services/user_session_context_service.py +0 -85
  150. {iatoolkit-0.3.9.dist-info → iatoolkit-0.107.4.dist-info}/WHEEL +0 -0
@@ -1,46 +1,53 @@
1
1
  Eres un asistente que responde preguntas o ejecuta tareas según el contexto de la empresa.
2
2
 
3
3
  ### **Nombre de la empresa**
4
- ## Nombre: {{company.name}}
4
+ ## Nombre: {{company}}, tambien se conoce como **{{ company_short_name }}**
5
+ ## Idioma: el idioma en que debes responder inicialmente es: {{language}}
5
6
 
6
7
  ### ** Información del usuario que esta consultando este chat**
7
- Contexto del usuario:
8
+ - Identificador unico de usuario: {{ user_identifier }}
8
9
  - Nombre: {{ user_fullname }}
9
10
  - Email: {{ user_email }}
10
11
  - Tipo de usuario: {% if user_is_local %}Interno{% else %}Externo{% endif %}
11
- - Empresa: {{ company_name }}
12
+ - Rol de usuario: {{ user_rol }}
12
13
 
13
- {% if user_name %}
14
- El usuario que consulta se identifica con la variable `user_name` y tiene el
15
- siguiente valor: {{ user_name }}.
16
14
 
17
- Este usuario tiene el rol: {{ user_rol }} en el producto {{ user_product }}.
15
+ ## 🔧 Servicios de datos disponibles en {{ company.name }}
18
16
 
19
- {% else %}
20
- El usuario que consulta se identifica como: {{ user_id }}
21
- {% endif %}
17
+ A continuación se muestran los *function calls* (tools) que puedes usar para resolver tareas relacionadas con datos.
18
+ Cada servicio incluye su **nombre**, **propósito** y **parámetros esperados**.
22
19
 
23
- ## Servicios de datos (function calls) disponibles en {{company.name}}:
20
+ ### 📌 LISTA DE TOOLS DISPONIBLES
24
21
  {% for service in service_list %}
25
- # servicio {{ loop.index }}: {{ service.name }}
22
+ #### Tool {{ loop.index }}: `{{ service.name }}`
23
+ - **Descripción:** {{ service.description }}
24
+ - **Parámetros:** {{ service.parameters | tojson }}
26
25
  {% endfor %}
27
26
 
27
+ ---
28
+
29
+ ### ⚠️ REGLAS IMPORTANTES PARA EL USO DE TOOLS
30
+
31
+ 1. **Debes usar un tool cuando la tarea lo requiera.**
32
+ Ejemplos:
33
+ - Consultar bases de datos
34
+ - Obtener información corporativa
35
+ - Ejecutar SQL
36
+ - Cargar documentos
37
+
38
+ 2. **NO inventes información** si un tool existe para obtenerla.
39
+ 3. Si un usuario solicita datos específicos, **elige el tool cuyo propósito coincida exactamente** con la solicitud.
40
+ 4. **Si ningún tool aplica**, responde siguiendo el estilo de un asistente normal.
41
+
42
+ ---
43
+
28
44
  Eres un asistente que responde preguntas sobre empresas y sus clientes.
29
45
 
30
46
  **Reglas obligatorias de contexto:**
31
- 1. Cada vez que el usuario consulte por un cliente (ya sea por RUT o nombre),
32
- debes memorizarlo y usarlo como cliente de contexto.
33
- 2. Si el usuario hace una pregunta **sin especificar un cliente** (sin RUT ni nombre),
34
- siempre debes asumir que la pregunta se refiere al **último cliente identificado** en la conversación.
35
- 3. Nunca cambies de cliente de contexto a menos que el usuario especifique uno nuevo.
36
- 4. Si el usuario pregunta por un cliente que no está en tus registros, responde indicando que no tienes información, pero **no borres el contexto anterior**.
37
- 5. No respondas con “no se encontró información del cliente” salvo que nunca se haya identificado ningún cliente antes en la conversación.
38
- 6. No debes incluir explicaciones, comentarios o texto adicional.
47
+ En caso que te hagan preguntas especificas sobre un cliente, debes asumir que la pregunta se
48
+ refiere al **último cliente identificado** en la conversación.
39
49
 
40
50
  **IMPORTANTE:**
41
- Si el usuario no menciona explícitamente nombre ni RUT en la pregunta, SIEMPRE responde usando el **último cliente** del que se obtuvo información.
42
-
43
- No respondas nunca sobre un cliente anterior si ya se identificó uno nuevo, y nunca pierdas el contexto salvo que el usuario lo cambie explícitamente.
44
51
 
45
52
  ### **Instrucciones**
46
53
  1. Devuelve siempre la respuesta en formato JSON.
@@ -4,6 +4,7 @@
4
4
  - Muchas columnas contienen información en formato **jsonb**.
5
5
  - Todas las consultas deben ser **sintácticamente correctas** y compatibles con PostgreSQL.
6
6
 
7
+ ## 🔍 Uso correcto de campos JSONB
7
8
  ## 🔍 Uso correcto de campos JSONB
8
9
 
9
10
  ### Regla central: ¡NO ASUMAS QUE LAS CLAVES JSONB SON COLUMNAS!
@@ -108,32 +109,66 @@ Ejemplo incorrecto: SUM(value->>'monto')::NUMERIC
108
109
  - Usa `TO_DATE(...)` para convertir el string a fecha:
109
110
  ```sql
110
111
  TO_DATE(jsonb_credit->>'created', 'DD-MM-YYYY')
111
- ´´´
112
+ ```
112
113
 
113
114
  - antes de comparar con `CURRENT_DATE` un campo de tipo jsonb,
114
115
  **siempre** castea explícitamente
115
116
 
116
- - No utilices TO_DATE si la columna ya es de tipo DATE o TIMESTAMP.
117
- Úsala solo si el campo es tipo TEXT o VARCHAR, y asegúrate de castearlo explícitamente si es necesario.
118
- TO_DATE espera un string, no una fecha ya en formato DATE, y causará un error de tipo de datos.
119
-
120
117
  - Si comparas fechas, asegúrate que ambos lados sean del mismo tipo.
121
118
  ejemplo:
122
119
  ```sql
123
- -- Si 'init_date' es DATE:
124
- TO_CHAR(init_date, 'DD-MM-YYYY') = ...
125
- -- Si 'created' es texto en formato 'DD-MM-YYYY':
126
- TO_DATE(jsonb_guarantee->>'created', 'DD-MM-YYYY')
120
+ -- Si 'buyer__date' es DATE:
121
+ TO_CHAR('buyer__date' , 'DD-MM-YYYY')
122
+ -- Si 'created_date' es texto en formato 'DD-MM-YYYY':
123
+ TO_DATE(jsonb_guarantee->>'created_date', 'DD-MM-YYYY')
127
124
  - Evitar comparar campos de tipo DATE o TIMESTAMP con strings generados por TO_CHAR.
128
125
  Usar funciones como date_trunc() o INTERVAL directamente.
129
126
 
127
+ 🎯 Regla para fechas que pueden venir vacías (solo TEXT / JSONB)
128
+
129
+ - Usa `TO_DATE()` ÚNICAMENTE cuando el valor de fecha sea un string:
130
+ - campos de tipo TEXT o VARCHAR
131
+ - o valores extraídos de JSONB con `->>` (por ejemplo `jsonb_credit->>'created'`).
132
+
133
+ - Si el campo de fecha es TEXTO y puede venir vacío (`''`), utiliza este patrón:
134
+ TO_DATE(NULLIF(campo_texto, ''), 'DD-MM-YYYY')
135
+
136
+ donde `campo_texto` debe ser SIEMPRE:
137
+ - un TEXT/VARCHAR, o
138
+ - una expresión de JSONB como `jsonb_column->>'field'`.
139
+
140
+ - **NUNCA** uses este patrón sobre columnas que ya son de tipo DATE o TIMESTAMP.
141
+ Ejemplos prohibidos:
142
+ - TO_DATE(NULLIF(bc.init_date, ''), 'DD-MM-YYYY')
143
+ - TO_DATE(NULLIF(c.init_date, ''), 'DD-MM-YYYY')
144
+ - TO_DATE(bc.init_date, 'DD-MM-YYYY')
145
+ - TO_DATE(c.init_date, 'DD-MM-YYYY')
146
+
147
+ - Para columnas DATE del esquema BCU (como ejemplo):
148
+ - `bcu_certificate.init_date`
149
+ - `bcu_certificate.end_date`
150
+ - `bcu_tender.adjudication_date`
151
+ - `bcu_tender.closing_date`
152
+
153
+ **Nunca** las envuelvas en `TO_DATE()` ni en `NULLIF(columna, '')`.
154
+ Deben usarse directamente:
155
+ - ORDER BY bc.init_date DESC NULLS LAST
156
+ - ORDER BY t.adjudication_date DESC NULLS LAST
157
+
130
158
  - Cuando necesites construir fechas a partir de valores numéricos
131
159
  (por ejemplo, año y mes), utiliza TO_DATE() con un string en formato 'YYYY-MM-DD',
132
160
  no uses concatenación sobre literales de tipo DATE.
133
161
 
134
- - Si el campo de fecha puede venir vacío (''), usa:
135
- TO_DATE(NULLIF(campo, ''), 'DD-MM-YYYY')
136
- Esto evita errores de conversión cuando la fecha está ausente o mal registrada.
162
+ - No utilices TO_DATE si la columna ya es de tipo DATE o TIMESTAMP.
163
+ Úsala solo si el campo es tipo TEXT o VARCHAR (por ejemplo, campos que vienen de JSONB con `->>`),
164
+ y siempre recuerda que TO_DATE espera un string.
165
+
166
+ - Si el campo de fecha **es texto** y puede venir vacío (`''`), usa:
167
+ TO_DATE(NULLIF(campo_texto, ''), 'DD-MM-YYYY')
168
+ Esto solo aplica cuando `campo_texto` es `TEXT`/`VARCHAR` o viene de `jsonb->>'campo'`.
169
+ **Nunca** uses este patrón sobre columnas que ya son DATE o TIMESTAMP
170
+ (en esos casos simplemente ordena o filtra con la columna tal cual, usando
171
+ `ORDER BY columna_fecha DESC NULLS LAST`).
137
172
 
138
173
  ### operaciones sobre campos de tipo JSONB
139
174
  - Cuando uses SUM o alguna otra función agregada sobre un valor extraído
@@ -0,0 +1,45 @@
1
+ {# El div principal ahora es un contenedor y tiene los estilos y clases de alineación #}
2
+ <div class="custom-company-header container d-flex justify-content-between align-items-center">
3
+
4
+ {% if company_short_name and branding %}
5
+ <a href="{{ url_for('home', company_short_name=company_short_name, lang=request.args.get('lang', 'en')) }}"
6
+ class="brand-name"
7
+ style="{{ branding.primary_text_style }}">
8
+ {{ branding.name }} IA
9
+ </a>
10
+ {% else %}
11
+ <span class="brand-name">
12
+ IAToolkit
13
+ </span>
14
+ {% endif %}
15
+
16
+ {# Contenedor derecho: powered-by a la izquierda y selector de idioma a la derecha #}
17
+ <div class="d-flex align-items-center ms-auto">
18
+ <div class="me-3 d-flex align-items-center">
19
+ <span class="powered-by">
20
+ Powered by <a href="http://www.iatoolkit.com" rel="noopener noreferrer" class="iatoolkit-link">IAToolkit</a>
21
+ </span>
22
+ </div>
23
+
24
+ {% set current_lang = request.args.get('lang', 'en') %}
25
+ {% set has_endpoint = request.endpoint is not none %}
26
+
27
+ {% if has_endpoint %}
28
+ {% if request.view_args %}
29
+ {% set href_en = url_for(request.endpoint, lang='en', **request.view_args) %}
30
+ {% set href_es = url_for(request.endpoint, lang='es', **request.view_args) %}
31
+ {% else %}
32
+ {% set href_en = url_for(request.endpoint, lang='en') %}
33
+ {% set href_es = url_for(request.endpoint, lang='es') %}
34
+ {% endif %}
35
+ {% endif %}
36
+
37
+ <div class="language-switcher d-flex align-items-center">
38
+ <a href="{{ href_en }}" class="language-link {{ 'active' if current_lang == 'en' else '' }}">EN</a>
39
+ <span class="mx-1">|</span>
40
+ <a href="{{ href_es }}" class="language-link {{ 'active' if current_lang == 'es' else '' }}">ES</a>
41
+ </div>
42
+ </div>
43
+
44
+
45
+ </div>
@@ -0,0 +1,42 @@
1
+ <div class="branded-form-container">
2
+ <!-- 1. Encabezado de Marketing -->
3
+ <div class="text-center mb-4">
4
+ <p class="text-muted widget-intro-text">
5
+ {{ t('ui.login_widget.welcome_message') }}
6
+ </p>
7
+ </div>
8
+
9
+ <!-- 2. Formulario de Inicio de Sesión -->
10
+ <form id="login-form"
11
+ action="{{ url_for('login', company_short_name=company_short_name, lang=request.args.get('lang', 'en')) }}"
12
+ method="post">
13
+ <div class="mb-3">
14
+ <label for="email" class="form-label d-block">{{ t('ui.signup.email_label') }}</label>
15
+ <input type="email" id="email" name="email" class="form-control"
16
+ required value="{{ form_data.email if form_data is defined else '' }}">
17
+ </div>
18
+ <div class="mb-3">
19
+ <label for="password" class="form-label d-block">{{ t('ui.signup.password_label') }}</label>
20
+ <input type="password" id="password" name="password"
21
+ class="form-control" required>
22
+ </div>
23
+ <button type="submit" class="btn btn-branded-primary w-100 fw-bold py-2">
24
+ {{ t('ui.login_widget.login_button') }}
25
+ </button>
26
+ </form>
27
+
28
+ <!-- 3. Nueva Sección de Registro más Atractiva -->
29
+ <div class="mt-4 pt-3 text-center" style="border-top: 1px solid #e0e0e0;">
30
+ <span class="text-muted small">{{ t('ui.login_widget.no_account_prompt') }}</span>
31
+ <a href="{{ url_for('signup', company_short_name=company_short_name, lang=request.args.get('lang', 'en')) }}" id="signup-link"
32
+ class="fw-bold ms-1 text-decoration-none" style="color: var(--brand-primary-color);">
33
+ {{ t('ui.login_widget.signup_link') }}</a>
34
+ </div>
35
+
36
+ <!-- 4. Enlace de Recuperación de Contraseña (más sutil) -->
37
+ <div class="text-center mt-2">
38
+ <a href="{{ url_for('forgot_password', company_short_name=company_short_name, lang=request.args.get('lang', 'en')) }}" class="text-decoration-none text-muted" style="font-size: 0.8rem;">
39
+ {{ t('ui.login_widget.forgot_password_link') }}
40
+ </a>
41
+ </div>
42
+ </div>
@@ -0,0 +1,78 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ {% if google_analytics_id %}
5
+ <!-- Google tag (gtag.js) -->
6
+ <script async src="https://www.googletagmanager.com/gtag/js?id=G-96G1XCM2CC"></script>
7
+ <script>
8
+ window.dataLayer = window.dataLayer || [];
9
+ function gtag(){dataLayer.push(arguments);}
10
+ gtag('js', new Date());
11
+
12
+ gtag('config', '{{ google_analytics_id }}');
13
+ </script>
14
+ {% endif %}
15
+
16
+
17
+ <meta charset="UTF-8">
18
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
19
+ <title>{% block title %}Chatbot{% endblock %}</title>
20
+
21
+ <!-- Bootstrap 5 CSS -->
22
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css">
23
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css" >
24
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/filepond/dist/filepond.min.css">
25
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css">
26
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css" />
27
+ <link rel="stylesheet" href="{{ url_for('static', filename='styles/chat_iatoolkit.css', _external=True) }}">
28
+ <link rel="stylesheet" href="{{ url_for('static', filename='styles/onboarding.css', _external=True) }}">
29
+ <link rel="stylesheet" href="{{ url_for('static', filename='styles/chat_modal.css', _external=True) }}">
30
+ <link rel="stylesheet" href="{{ url_for('static', filename='styles/llm_output.css', _external=True) }}">
31
+
32
+ {% block styles %}{% endblock %}
33
+ </head>
34
+ <body>
35
+ <!-- El "Ancla": Envolvemos el contenido en un div con un ID y estilo. -->
36
+ <div id="page-content-wrapper" style="position: relative;">
37
+ {% block content %}{% endblock %}
38
+ </div>
39
+
40
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js"></script>
41
+ <script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
42
+ <script src="https://cdn.jsdelivr.net/npm/filepond/dist/filepond.min.js"></script>
43
+ <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
44
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"></script>
45
+
46
+ <!-- Mostrar alertas SweetAlert2 si existe el mensaje -->
47
+ <script>
48
+ // Configuración global de Toastr
49
+ toastr.options = {
50
+ "closeButton": true,
51
+ "progressBar": true,
52
+ "positionClass": "toast-bottom-right",
53
+ "preventDuplicates": true,
54
+ "timeOut": "7000",
55
+ "extendedTimeOut": "1000",
56
+ "tapToDismiss": false,
57
+ "target": "#page-content-wrapper"
58
+ };
59
+
60
+ {% if flashed_messages %}
61
+ {% for category, message in flashed_messages %}
62
+ var toastClass = 'toast-info'; // default class
63
+ if ('{{ category }}' === 'error') {
64
+ toastClass = 'toast-error';
65
+ } else if ('{{ category }}' === 'success') {
66
+ toastClass = 'toast-success';
67
+ }
68
+
69
+ // Llama a Toastr usando la opción 'toastClass' para aplicar nuestro estilo
70
+ toastr.info("{{ message }}", null, { "toastClass": "toast " + toastClass });
71
+
72
+ {% endfor %}
73
+ {% endif %}
74
+ </script>
75
+
76
+ {% block scripts %}{% endblock %}
77
+ </body>
78
+ </html>
@@ -0,0 +1,66 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}{{ t('ui.change_password.title') }} - {{ branding.name }} {% endblock %}
4
+
5
+ {% block styles %}
6
+ <style>
7
+ {{ branding.css_variables | safe }}
8
+ </style>
9
+ <link rel="stylesheet" href="{{ url_for('static', filename='styles/chat_public.css') }}">
10
+ {% endblock %}
11
+
12
+ {% block content %}
13
+ <div class="container mt-4">
14
+
15
+ {% include '_company_header.html' %}
16
+
17
+ <!-- Sección contenedora para centrar el contenido -->
18
+ <section class="hero-section">
19
+ <div class="container">
20
+ <div class="row justify-content-center">
21
+ <div class="col-lg-6 col-md-8">
22
+ <div class="branded-form-container">
23
+ <h4 class="branded-form-title">{{ t('ui.change_password.title') }}</h4>
24
+ <p class="text-muted text-center mb-4">
25
+ {{ t('ui.change_password.subtitle', email=email) | safe }}
26
+ </p>
27
+
28
+ <form action="{{ url_for('change_password', company_short_name=company_short_name, token=token) }}" method="post">
29
+
30
+ <div class="mb-3">
31
+ <label for="temp_code" class="form-label text-secondary">{{ t('ui.change_password.temp_code_label') }}</label>
32
+ <input type="text" id="temp_code" name="temp_code" class="form-control"
33
+ required value="{{ form_data.temp_code if form_data else '' }}"
34
+ placeholder="{{ t('ui.change_password.temp_code_placeholder') }}">
35
+ </div>
36
+
37
+ <div class="mb-3">
38
+ <label for="new_password" class="form-label text-secondary">{{ t('ui.change_password.new_password_label') }}</label>
39
+ <input type="password" id="new_password" name="new_password" class="form-control" required>
40
+ <div class="d-flex align-items-start text-muted mt-2" style="font-size: 0.8rem;">
41
+ <i class="bi bi-info-circle me-2" style="font-size: 0.9rem; line-height: 1.4;"></i>
42
+ <span>{{ t('ui.change_password.password_instructions') }}</span>
43
+ </div>
44
+ </div>
45
+
46
+ <div class="mb-3">
47
+ <label for="confirm_password" class="form-label text-secondary">{{ t('ui.change_password.confirm_password_label') }}</label>
48
+ <input type="password" id="confirm_password" name="confirm_password" class="form-control" required>
49
+ </div>
50
+
51
+ <button type="submit" class="btn btn-branded-primary w-100 fw-bold py-2 mt-3">{{ t('ui.change_password.save_button') }}</button>
52
+ </form>
53
+
54
+ <div class="text-center mt-4 pt-3" style="border-top: 1px solid #e0e0e0;">
55
+ <a href="{{ url_for('home', company_short_name=company_short_name) }}" class="text-muted text-decoration-none fw-semibold">
56
+ <i class="bi bi-arrow-left me-1"></i>{{ t('ui.change_password.back_to_home') }}
57
+ </a>
58
+ </div>
59
+ </div>
60
+ </div>
61
+ </div>
62
+ </div>
63
+ </section>
64
+ </div>
65
+
66
+ {% endblock %}