iatoolkit 0.62.0__tar.gz → 0.64.0__tar.gz
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-0.62.0 → iatoolkit-0.64.0}/PKG-INFO +1 -1
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/pyproject.toml +1 -1
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/common/routes.py +5 -2
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/iatoolkit.py +1 -1
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/services/branding_service.py +28 -22
- iatoolkit-0.64.0/src/iatoolkit/services/help_content_service.py +30 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/services/user_feedback_service.py +1 -1
- iatoolkit-0.64.0/src/iatoolkit/static/js/chat_feedback_button.js +82 -0
- iatoolkit-0.64.0/src/iatoolkit/static/js/chat_help_content.js +124 -0
- iatoolkit-0.64.0/src/iatoolkit/static/js/chat_history_button.js +93 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/static/js/chat_main.js +3 -2
- iatoolkit-0.64.0/src/iatoolkit/static/js/chat_reload_button.js +35 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/static/styles/chat_iatoolkit.css +156 -169
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/static/styles/chat_modal.css +97 -93
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/static/styles/landing_page.css +16 -10
- iatoolkit-0.64.0/src/iatoolkit/templates/_company_header.html +20 -0
- iatoolkit-0.64.0/src/iatoolkit/templates/_login_widget.html +40 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/templates/change_password.html +9 -11
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/templates/chat.html +16 -8
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/templates/chat_modals.html +91 -66
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/templates/error.html +9 -13
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/templates/index.html +13 -14
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/templates/onboarding_shell.html +0 -1
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/views/change_password_view.py +5 -3
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/views/forgot_password_view.py +9 -5
- iatoolkit-0.64.0/src/iatoolkit/views/help_content_api_view.py +50 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/views/history_api_view.py +0 -1
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/views/home_view.py +2 -4
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/views/login_view.py +14 -8
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/views/signup_view.py +9 -5
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/views/verify_user_view.py +0 -1
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit.egg-info/PKG-INFO +1 -1
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit.egg-info/SOURCES.txt +4 -0
- iatoolkit-0.62.0/src/iatoolkit/static/js/chat_feedback_button.js +0 -110
- iatoolkit-0.62.0/src/iatoolkit/static/js/chat_history_button.js +0 -127
- iatoolkit-0.62.0/src/iatoolkit/static/js/chat_reload_button.js +0 -52
- iatoolkit-0.62.0/src/iatoolkit/templates/_company_header.html +0 -21
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/readme.md +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/requirements.txt +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/setup.cfg +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/__init__.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/base_company.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/cli_commands.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/common/__init__.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/common/exceptions.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/common/session_manager.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/common/util.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/company_registry.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/__init__.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/call_service.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/connectors/__init__.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/connectors/file_connector.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/connectors/file_connector_factory.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/connectors/google_cloud_storage_connector.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/connectors/google_drive_connector.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/connectors/local_file_connector.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/connectors/s3_connector.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/gemini_adapter.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/google_chat_app.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/llm_client.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/llm_proxy.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/llm_response.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/mail_app.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/openai_adapter.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/redis_session_manager.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/repositories/__init__.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/repositories/database_manager.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/repositories/document_repo.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/repositories/llm_query_repo.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/repositories/models.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/repositories/profile_repo.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/repositories/tasks_repo.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/repositories/vs_repo.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/services/__init__.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/services/auth_service.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/services/benchmark_service.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/services/dispatcher_service.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/services/document_service.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/services/excel_service.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/services/file_processor_service.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/services/history_service.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/services/jwt_service.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/services/load_documents_service.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/services/mail_service.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/services/onboarding_service.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/services/profile_service.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/services/prompt_manager_service.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/services/query_service.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/services/search_service.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/services/sql_service.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/services/tasks_service.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/services/user_session_context_service.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/static/images/fernando.jpeg +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/static/js/chat_filepond.js +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/static/js/chat_logout_button.js +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/static/js/chat_onboarding_button.js +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/static/js/chat_prompt_manager.js +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/static/styles/chat_info.css +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/static/styles/llm_output.css +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/static/styles/onboarding.css +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/system_prompts/format_styles.prompt +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/system_prompts/query_main.prompt +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/system_prompts/sql_rules.prompt +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/templates/about.html +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/templates/base.html +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/templates/forgot_password.html +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/templates/header.html +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/templates/login_simulation.html +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/templates/signup.html +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/templates/test.html +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/views/__init__.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/views/base_login_view.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/views/external_login_view.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/views/file_store_api_view.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/views/index_view.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/views/init_context_api_view.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/views/llmquery_api_view.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/views/login_simulation_view.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/views/logout_api_view.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/views/prompt_api_view.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/views/tasks_api_view.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/views/tasks_review_api_view.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit/views/user_feedback_api_view.py +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit.egg-info/dependency_links.txt +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit.egg-info/requires.txt +0 -0
- {iatoolkit-0.62.0 → iatoolkit-0.64.0}/src/iatoolkit.egg-info/top_level.txt +0 -0
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
from flask import render_template, redirect, url_for,send_from_directory, current_app, abort
|
|
7
7
|
from flask import jsonify
|
|
8
|
-
from iatoolkit.views.history_api_view import HistoryApiView
|
|
9
8
|
|
|
10
9
|
|
|
11
10
|
# this function register all the views
|
|
@@ -24,6 +23,9 @@ def register_views(injector, app):
|
|
|
24
23
|
from iatoolkit.views.file_store_api_view import FileStoreApiView
|
|
25
24
|
from iatoolkit.views.user_feedback_api_view import UserFeedbackApiView
|
|
26
25
|
from iatoolkit.views.prompt_api_view import PromptApiView
|
|
26
|
+
from iatoolkit.views.history_api_view import HistoryApiView
|
|
27
|
+
from iatoolkit.views.help_content_api_view import HelpContentApiView
|
|
28
|
+
|
|
27
29
|
from iatoolkit.views.login_view import LoginView, FinalizeContextView
|
|
28
30
|
from iatoolkit.views.external_login_view import ExternalLoginView, RedeemTokenApiView
|
|
29
31
|
from iatoolkit.views.logout_api_view import LogoutApiView
|
|
@@ -79,9 +81,10 @@ def register_views(injector, app):
|
|
|
79
81
|
# open the promt directory
|
|
80
82
|
app.add_url_rule('/<company_short_name>/api/prompts', view_func=PromptApiView.as_view('prompt'))
|
|
81
83
|
|
|
82
|
-
#
|
|
84
|
+
# toolbar buttons
|
|
83
85
|
app.add_url_rule('/<company_short_name>/api/feedback', view_func=UserFeedbackApiView.as_view('feedback'))
|
|
84
86
|
app.add_url_rule('/<company_short_name>/api/history', view_func=HistoryApiView.as_view('history'))
|
|
87
|
+
app.add_url_rule('/<company_short_name>/api/help-content', view_func=HelpContentApiView.as_view('help-content'))
|
|
85
88
|
|
|
86
89
|
# tasks management endpoints: create task, and review answer
|
|
87
90
|
app.add_url_rule('/tasks', view_func=TaskApiView.as_view('tasks'))
|
|
@@ -19,7 +19,7 @@ from werkzeug.middleware.proxy_fix import ProxyFix
|
|
|
19
19
|
from injector import Binder, Injector, singleton
|
|
20
20
|
from importlib.metadata import version as _pkg_version, PackageNotFoundError
|
|
21
21
|
|
|
22
|
-
IATOOLKIT_VERSION = "0.
|
|
22
|
+
IATOOLKIT_VERSION = "0.64.0"
|
|
23
23
|
|
|
24
24
|
# global variable for the unique instance of IAToolkit
|
|
25
25
|
_iatoolkit_instance: Optional['IAToolkit'] = None
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
# IAToolkit is open source software.
|
|
5
5
|
|
|
6
6
|
from iatoolkit.repositories.models import Company
|
|
7
|
+
from injector import inject
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
class BrandingService:
|
|
@@ -11,6 +12,7 @@ class BrandingService:
|
|
|
11
12
|
Servicio centralizado que gestiona la configuración de branding.
|
|
12
13
|
"""
|
|
13
14
|
|
|
15
|
+
@inject
|
|
14
16
|
def __init__(self):
|
|
15
17
|
"""
|
|
16
18
|
Define los estilos de branding por defecto para la aplicación.
|
|
@@ -19,13 +21,16 @@ class BrandingService:
|
|
|
19
21
|
# --- Estilos del Encabezado Principal ---
|
|
20
22
|
"header_background_color": "#FFFFFF",
|
|
21
23
|
"header_text_color": "#6C757D",
|
|
22
|
-
"primary_font_weight": "
|
|
23
|
-
"primary_font_size": "
|
|
24
|
-
"secondary_font_weight": "
|
|
25
|
-
"secondary_font_size": "0.
|
|
26
|
-
"tertiary_font_weight": "
|
|
27
|
-
"tertiary_font_size": "0.
|
|
28
|
-
"tertiary_opacity": "0.
|
|
24
|
+
"primary_font_weight": "600",
|
|
25
|
+
"primary_font_size": "1.2rem",
|
|
26
|
+
"secondary_font_weight": "400",
|
|
27
|
+
"secondary_font_size": "0.9rem",
|
|
28
|
+
"tertiary_font_weight": "300",
|
|
29
|
+
"tertiary_font_size": "0.8rem",
|
|
30
|
+
"tertiary_opacity": "0.7",
|
|
31
|
+
|
|
32
|
+
# headings
|
|
33
|
+
"brand_text_heading_color": "#334155", # Gris pizarra por defecto
|
|
29
34
|
|
|
30
35
|
# Estilos Globales de la Marca ---
|
|
31
36
|
"brand_primary_color": "#0d6efd", # Azul de Bootstrap por defecto
|
|
@@ -40,25 +45,27 @@ class BrandingService:
|
|
|
40
45
|
"brand_danger_border": "#f5c2c7", # Borde rojo intermedio
|
|
41
46
|
|
|
42
47
|
# Estilos para Alertas Informativas ---
|
|
43
|
-
"brand_info_bg": "#
|
|
44
|
-
"brand_info_text": "#
|
|
45
|
-
"brand_info_border": "#
|
|
48
|
+
"brand_info_bg": "#F0F4F8", # Un fondo de gris azulado muy pálido
|
|
49
|
+
"brand_info_text": "#0d6efd", # Texto en el color primario
|
|
50
|
+
"brand_info_border": "#D9E2EC", # Borde de gris azulado pálido
|
|
46
51
|
|
|
47
52
|
# Estilos para el Asistente de Prompts ---
|
|
48
53
|
"prompt_assistant_bg": "#f8f9fa",
|
|
49
54
|
"prompt_assistant_border": "#dee2e6",
|
|
50
|
-
"prompt_assistant_icon_color": "#6c757d",
|
|
51
55
|
"prompt_assistant_button_bg": "#FFFFFF",
|
|
52
56
|
"prompt_assistant_button_text": "#495057",
|
|
53
57
|
"prompt_assistant_button_border": "#ced4da",
|
|
54
58
|
"prompt_assistant_dropdown_bg": "#f8f9fa",
|
|
55
59
|
"prompt_assistant_header_bg": "#e9ecef",
|
|
56
60
|
"prompt_assistant_header_text": "#495057",
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
|
|
62
|
+
# this use the primary by default
|
|
63
|
+
"prompt_assistant_icon_color": None,
|
|
64
|
+
"prompt_assistant_item_hover_bg": None,
|
|
65
|
+
"prompt_assistant_item_hover_text": None,
|
|
59
66
|
|
|
60
67
|
# Color para el botón de Enviar ---
|
|
61
|
-
"send_button_color": "#212529"
|
|
68
|
+
"send_button_color": "#212529" # Gris oscuro/casi negro por defecto
|
|
62
69
|
}
|
|
63
70
|
|
|
64
71
|
def get_company_branding(self, company: Company | None) -> dict:
|
|
@@ -80,10 +87,6 @@ class BrandingService:
|
|
|
80
87
|
secondary_rgb = hex_to_rgb(final_branding_values['brand_secondary_color'])
|
|
81
88
|
|
|
82
89
|
# --- CONSTRUCCIÓN DE ESTILOS Y VARIABLES CSS ---
|
|
83
|
-
header_style = (
|
|
84
|
-
f"background-color: {final_branding_values['header_background_color']}; "
|
|
85
|
-
f"color: {final_branding_values['header_text_color']};"
|
|
86
|
-
)
|
|
87
90
|
primary_text_style = (
|
|
88
91
|
f"font-weight: {final_branding_values['primary_font_weight']}; "
|
|
89
92
|
f"font-size: {final_branding_values['primary_font_size']};"
|
|
@@ -103,6 +106,10 @@ class BrandingService:
|
|
|
103
106
|
:root {{
|
|
104
107
|
--brand-primary-color: {final_branding_values['brand_primary_color']};
|
|
105
108
|
--brand-secondary-color: {final_branding_values['brand_secondary_color']};
|
|
109
|
+
--brand-header-bg: {final_branding_values['header_background_color']};
|
|
110
|
+
--brand-header-text: {final_branding_values['header_text_color']};
|
|
111
|
+
--brand-text-heading-color: {final_branding_values['brand_text_heading_color']};
|
|
112
|
+
|
|
106
113
|
--brand-primary-color-rgb: {', '.join(map(str, primary_rgb))};
|
|
107
114
|
--brand-secondary-color-rgb: {', '.join(map(str, secondary_rgb))};
|
|
108
115
|
--brand-text-on-primary: {final_branding_values['brand_text_on_primary']};
|
|
@@ -114,11 +121,11 @@ class BrandingService:
|
|
|
114
121
|
--brand-danger-text: {final_branding_values['brand_danger_text']};
|
|
115
122
|
--brand-danger-border: {final_branding_values['brand_danger_border']};
|
|
116
123
|
--brand-info-bg: {final_branding_values['brand_info_bg']};
|
|
117
|
-
--brand-info-text: {final_branding_values['brand_info_text']};
|
|
124
|
+
--brand-info-text: {final_branding_values['brand_info_text'] or final_branding_values['brand_primary_color']};
|
|
118
125
|
--brand-info-border: {final_branding_values['brand_info_border']};
|
|
119
126
|
--brand-prompt-assistant-bg: {final_branding_values['prompt_assistant_bg']};
|
|
120
127
|
--brand-prompt-assistant-border: {final_branding_values['prompt_assistant_border']};
|
|
121
|
-
--brand-prompt-assistant-icon-color: {final_branding_values['prompt_assistant_icon_color']};
|
|
128
|
+
--brand-prompt-assistant-icon-color: {final_branding_values['prompt_assistant_icon_color'] or final_branding_values['brand_primary_color']};
|
|
122
129
|
--brand-prompt-assistant-button-bg: {final_branding_values['prompt_assistant_button_bg']};
|
|
123
130
|
--brand-prompt-assistant-button-text: {final_branding_values['prompt_assistant_button_text']};
|
|
124
131
|
--brand-prompt-assistant-button-border: {final_branding_values['prompt_assistant_button_border']};
|
|
@@ -133,11 +140,10 @@ class BrandingService:
|
|
|
133
140
|
|
|
134
141
|
return {
|
|
135
142
|
"name": company.name if company else "IAToolkit",
|
|
136
|
-
"header_style": header_style,
|
|
137
143
|
"primary_text_style": primary_text_style,
|
|
138
144
|
"secondary_text_style": secondary_text_style,
|
|
139
145
|
"tertiary_text_style": tertiary_text_style,
|
|
140
146
|
"header_text_color": final_branding_values['header_text_color'],
|
|
141
147
|
"css_variables": css_variables,
|
|
142
|
-
"send_button_color": final_branding_values['
|
|
148
|
+
"send_button_color": final_branding_values['brand_primary_color']
|
|
143
149
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
+
# Product: IAToolkit
|
|
3
|
+
#
|
|
4
|
+
# IAToolkit is open source software.
|
|
5
|
+
|
|
6
|
+
from iatoolkit.common.util import Utility
|
|
7
|
+
from iatoolkit.common.exceptions import IAToolkitException
|
|
8
|
+
import os
|
|
9
|
+
from injector import inject
|
|
10
|
+
import logging
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class HelpContentService:
|
|
14
|
+
@inject
|
|
15
|
+
def __init__(self, util: Utility):
|
|
16
|
+
self.util = util
|
|
17
|
+
|
|
18
|
+
def get_content(self, company_short_name: str | None) -> dict:
|
|
19
|
+
filepath = f'companies/{company_short_name}/help_content.yaml'
|
|
20
|
+
if not os.path.exists(filepath):
|
|
21
|
+
return {}
|
|
22
|
+
|
|
23
|
+
# read the file
|
|
24
|
+
try:
|
|
25
|
+
help_content = self.util.load_schema_from_yaml(filepath)
|
|
26
|
+
return help_content
|
|
27
|
+
except Exception as e:
|
|
28
|
+
logging.exception(e)
|
|
29
|
+
raise IAToolkitException(IAToolkitException.ErrorType.CONFIG_ERROR,
|
|
30
|
+
f"Error obteniendo help de {company_short_name}: {str(e)}") from e
|
|
@@ -96,7 +96,7 @@ class UserFeedbackService:
|
|
|
96
96
|
logging.error(f"No se pudo guardar el feedback para el usuario {user_identifier} en la empresa {company_short_name}")
|
|
97
97
|
return {'error': 'No se pudo guardar el feedback'}
|
|
98
98
|
|
|
99
|
-
return {'message': 'Feedback guardado correctamente'}
|
|
99
|
+
return {'success': True, 'message': 'Feedback guardado correctamente'}
|
|
100
100
|
|
|
101
101
|
except Exception as e:
|
|
102
102
|
logging.exception(f"Error crítico en el servicio de feedback: {e}")
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
$(document).ready(function () {
|
|
2
|
+
$('#submit-feedback').on('click', function () {
|
|
3
|
+
sendFeedback(this);
|
|
4
|
+
});
|
|
5
|
+
|
|
6
|
+
// Evento para enviar el feedback
|
|
7
|
+
async function sendFeedback(submitButton) {
|
|
8
|
+
toastr.options = {"positionClass": "toast-bottom-right", "preventDuplicates": true};
|
|
9
|
+
const feedbackText = $('#feedback-text').val().trim();
|
|
10
|
+
const activeStars = $('.star.active').length;
|
|
11
|
+
|
|
12
|
+
if (!feedbackText) {
|
|
13
|
+
toastr.error('Por favor, escribe tu comentario antes de enviar.');
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (activeStars === 0) {
|
|
18
|
+
toastr.error('Por favor, califica al asistente con las estrellas.');
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
submitButton.disabled = true;
|
|
23
|
+
|
|
24
|
+
// call the IAToolkit API to send feedback
|
|
25
|
+
const data = {
|
|
26
|
+
"user_identifier": window.user_identifier,
|
|
27
|
+
"message": feedbackText,
|
|
28
|
+
"rating": activeStars,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const responseData = await callToolkit('/api/feedback', data, "POST");
|
|
32
|
+
if (responseData)
|
|
33
|
+
toastr.success('¡Gracias por tu comentario!', 'Feedback Enviado');
|
|
34
|
+
else
|
|
35
|
+
toastr.error('No se pudo enviar el feedback, por favor intente nuevamente.');
|
|
36
|
+
|
|
37
|
+
submitButton.disabled = false;
|
|
38
|
+
$('#feedbackModal').modal('hide');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Evento para abrir el modal de feedback
|
|
42
|
+
$('#send-feedback-button').on('click', function () {
|
|
43
|
+
$('#submit-feedback').prop('disabled', false);
|
|
44
|
+
$('#submit-feedback').html('<i class="bi bi-send me-1 icon-spaced"></i>Enviar');
|
|
45
|
+
$('.star').removeClass('active hover-active'); // Resetea estrellas
|
|
46
|
+
$('#feedback-text').val(''); // Limpia texto
|
|
47
|
+
$('.modal-body .alert').remove(); // Quita alertas previas
|
|
48
|
+
$('#feedbackModal').modal('show');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Evento que se dispara DESPUÉS de que el modal se ha ocultado
|
|
52
|
+
$('#feedbackModal').on('hidden.bs.modal', function () {
|
|
53
|
+
$('#feedback-text').val('');
|
|
54
|
+
$('.modal-body .alert').remove();
|
|
55
|
+
$('.star').removeClass('active');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Función para el sistema de estrellas
|
|
59
|
+
window.gfg = function (rating) {
|
|
60
|
+
$('.star').removeClass('active');
|
|
61
|
+
$('.star').each(function (index) {
|
|
62
|
+
if (index < rating) {
|
|
63
|
+
$(this).addClass('active');
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
$('.star').hover(
|
|
69
|
+
function () {
|
|
70
|
+
const rating = $(this).data('rating');
|
|
71
|
+
$('.star').removeClass('hover-active');
|
|
72
|
+
$('.star').each(function (index) {
|
|
73
|
+
if ($(this).data('rating') <= rating) {
|
|
74
|
+
$(this).addClass('hover-active');
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
},
|
|
78
|
+
function () {
|
|
79
|
+
$('.star').removeClass('hover-active');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
});
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
$(document).ready(function () {
|
|
2
|
+
|
|
3
|
+
let helpContent = null; // Variable para cachear el contenido de ayuda
|
|
4
|
+
|
|
5
|
+
// Evento de clic en el botón de ayuda
|
|
6
|
+
$('#open-help-button').on('click', async function () {
|
|
7
|
+
const helpModal = new bootstrap.Modal(document.getElementById('helpModal'));
|
|
8
|
+
const accordionContainer = $('#help-accordion-container');
|
|
9
|
+
const spinner = $('#help-spinner');
|
|
10
|
+
|
|
11
|
+
// Si el contenido no se ha cargado, hacer la llamada a la API
|
|
12
|
+
if (helpContent) {
|
|
13
|
+
// Si el contenido ya está cacheado, solo muestra el modal
|
|
14
|
+
helpModal.show();
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
spinner.show();
|
|
19
|
+
accordionContainer.hide();
|
|
20
|
+
helpModal.show();
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const helpContent = await callToolkit('/api/help-content', {}, "POST");
|
|
24
|
+
|
|
25
|
+
if (!helpContent) {
|
|
26
|
+
toastr.error('No se pudo cargar la guía de uso. Por favor, intente más tarde.');
|
|
27
|
+
spinner.hide();
|
|
28
|
+
helpModal.hide();
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Construir el HTML del acordeón y mostrarlo
|
|
33
|
+
buildHelpAccordion(helpContent);
|
|
34
|
+
spinner.hide();
|
|
35
|
+
accordionContainer.show();
|
|
36
|
+
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.error("Error al cargar el contenido de ayuda:", error);
|
|
39
|
+
toastr.error('Ocurrió un error de red al cargar la guía.');
|
|
40
|
+
spinner.hide();
|
|
41
|
+
helpModal.hide();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Construye dinámicamente el HTML para el acordeón de ayuda a partir de los datos.
|
|
47
|
+
* @param {object} data El objeto JSON con el contenido de ayuda.
|
|
48
|
+
*/
|
|
49
|
+
function buildHelpAccordion(data) {
|
|
50
|
+
const container = $('#help-accordion-container');
|
|
51
|
+
container.empty(); // Limpiar cualquier contenido previo
|
|
52
|
+
|
|
53
|
+
let accordionHtml = '';
|
|
54
|
+
|
|
55
|
+
if (data.data_sources) {
|
|
56
|
+
let contentHtml = '<dl>';
|
|
57
|
+
data.data_sources.forEach(p => {
|
|
58
|
+
contentHtml += `<dt>${p.source}</dt><dd>${p.description}</dd>`;
|
|
59
|
+
});
|
|
60
|
+
contentHtml += `</dl>`;
|
|
61
|
+
accordionHtml += createAccordionItem('sources', 'Datos disponibles', contentHtml, true);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (data.example_questions) {
|
|
65
|
+
let contentHtml = '';
|
|
66
|
+
data.example_questions.forEach(cat => {
|
|
67
|
+
contentHtml += `<h6>${cat.category}</h6><ul>`;
|
|
68
|
+
cat.questions.forEach(q => contentHtml += `<li>${q}</li>`);
|
|
69
|
+
contentHtml += `</ul>`;
|
|
70
|
+
});
|
|
71
|
+
accordionHtml += createAccordionItem('examples', 'Preguntas de Ejemplo', contentHtml);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (data.best_practices) {
|
|
75
|
+
let contentHtml = '<dl>';
|
|
76
|
+
data.best_practices.forEach(p => {
|
|
77
|
+
contentHtml += `<dt>${p.title}</dt><dd>${p.description}`;
|
|
78
|
+
if (p.example) {
|
|
79
|
+
contentHtml += `<br><small class="text-muted"><em>Ej: "${p.example}"</em></small>`;
|
|
80
|
+
}
|
|
81
|
+
contentHtml += `</dd>`;
|
|
82
|
+
});
|
|
83
|
+
contentHtml += `</dl>`;
|
|
84
|
+
accordionHtml += createAccordionItem('practices', 'Mejores Prácticas', contentHtml);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (data.capabilities) {
|
|
88
|
+
let contentHtml = `<div class="row">`;
|
|
89
|
+
contentHtml += `<div class="col-md-6"><h6>Puede hacer:</h6><ul>${data.capabilities.can_do.map(item => `<li>${item}</li>`).join('')}</ul></div>`;
|
|
90
|
+
contentHtml += `<div class="col-md-6"><h6>No puede hacer:</h6><ul>${data.capabilities.cannot_do.map(item => `<li>${item}</li>`).join('')}</ul></div>`;
|
|
91
|
+
contentHtml += `</div>`;
|
|
92
|
+
accordionHtml += createAccordionItem('capabilities', 'Capacidades y Límites', contentHtml);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
container.html(accordionHtml);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Helper para crear un item del acordeón de Bootstrap.
|
|
100
|
+
* @param {string} id El ID base para los elementos.
|
|
101
|
+
* @param {string} title El título que se muestra en el botón del acordeón.
|
|
102
|
+
* @param {string} contentHtml El HTML que va dentro del cuerpo colapsable.
|
|
103
|
+
* @param {boolean} isOpen Si el item debe estar abierto por defecto.
|
|
104
|
+
* @returns {string} El string HTML del item del acordeón.
|
|
105
|
+
*/
|
|
106
|
+
function createAccordionItem(id, title, contentHtml, isOpen = false) {
|
|
107
|
+
const showClass = isOpen ? 'show' : '';
|
|
108
|
+
const collapsedClass = isOpen ? '' : 'collapsed';
|
|
109
|
+
|
|
110
|
+
return `
|
|
111
|
+
<div class="accordion-item">
|
|
112
|
+
<h2 class="accordion-header" id="heading-${id}">
|
|
113
|
+
<button class="accordion-button ${collapsedClass}" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-${id}" aria-expanded="${isOpen}" aria-controls="collapse-${id}">
|
|
114
|
+
${title}
|
|
115
|
+
</button>
|
|
116
|
+
</h2>
|
|
117
|
+
<div id="collapse-${id}" class="accordion-collapse collapse ${showClass}" aria-labelledby="heading-${id}" data-bs-parent="#help-accordion-container">
|
|
118
|
+
<div class="accordion-body">
|
|
119
|
+
${contentHtml}
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
</div>`;
|
|
123
|
+
}
|
|
124
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
$(document).ready(function () {
|
|
2
|
+
// Evento para abrir el modal de historial
|
|
3
|
+
$('#history-button').on('click', function() {
|
|
4
|
+
loadHistory();
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
// Función para cargar el historial
|
|
9
|
+
async function loadHistory() {
|
|
10
|
+
const historyLoading = $('#history-loading');
|
|
11
|
+
|
|
12
|
+
historyLoading.show();
|
|
13
|
+
|
|
14
|
+
// cal the toolkit, handle the response and errors
|
|
15
|
+
const data = await callToolkit("/api/history", {}, "POST");
|
|
16
|
+
|
|
17
|
+
if (data && data.history) {
|
|
18
|
+
$('#historyModal').modal('show');
|
|
19
|
+
displayAllHistory(data.history);
|
|
20
|
+
$('#history-content').show();
|
|
21
|
+
}
|
|
22
|
+
historyLoading.hide();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Función para mostrar todo el historial
|
|
26
|
+
function displayAllHistory(historyData) {
|
|
27
|
+
const historyTableBody = $('#history-table-body');
|
|
28
|
+
|
|
29
|
+
historyTableBody.empty();
|
|
30
|
+
|
|
31
|
+
// Filtrar solo consultas que son strings simples
|
|
32
|
+
const filteredHistory = historyData.filter(item => {
|
|
33
|
+
try {
|
|
34
|
+
JSON.parse(item.query);
|
|
35
|
+
return false;
|
|
36
|
+
} catch (e) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Poblar la tabla
|
|
42
|
+
filteredHistory.forEach((item, index) => {
|
|
43
|
+
const icon = $('<i>').addClass('bi bi-pencil-fill');
|
|
44
|
+
|
|
45
|
+
const link = $('<a>')
|
|
46
|
+
.attr('href', 'javascript:void(0);')
|
|
47
|
+
.addClass('edit-pencil')
|
|
48
|
+
.attr('title', 'Copiar consulta al chat')
|
|
49
|
+
.data('query', item.query)
|
|
50
|
+
.append(icon);
|
|
51
|
+
|
|
52
|
+
const row = $('<tr>').append(
|
|
53
|
+
$('<td>').addClass('text-nowrap').text(formatDate(item.created_at)),
|
|
54
|
+
$('<td>').text(item.query),
|
|
55
|
+
$('<td>').append(link),
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
historyTableBody.append(row);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function formatDate(dateString) {
|
|
63
|
+
const date = new Date(dateString);
|
|
64
|
+
|
|
65
|
+
const padTo2Digits = (num) => num.toString().padStart(2, '0');
|
|
66
|
+
|
|
67
|
+
const day = padTo2Digits(date.getDate());
|
|
68
|
+
const month = padTo2Digits(date.getMonth() + 1);
|
|
69
|
+
const year = date.getFullYear();
|
|
70
|
+
const hours = padTo2Digits(date.getHours());
|
|
71
|
+
const minutes = padTo2Digits(date.getMinutes());
|
|
72
|
+
|
|
73
|
+
return `${day}-${month} ${hours}:${minutes}`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// event handler for the edit pencil icon
|
|
77
|
+
$('#history-table-body').on('click', '.edit-pencil', function() {
|
|
78
|
+
const queryText = $(this).data('query');
|
|
79
|
+
|
|
80
|
+
// copy the text to the chat input box
|
|
81
|
+
if (queryText) {
|
|
82
|
+
$('#question').val(queryText);
|
|
83
|
+
autoResizeTextarea($('#question')[0]);
|
|
84
|
+
$('#send-button').removeClass('disabled');
|
|
85
|
+
|
|
86
|
+
// Cerrar el modal
|
|
87
|
+
$('#historyModal').modal('hide');
|
|
88
|
+
|
|
89
|
+
// Hacer focus en el textarea
|
|
90
|
+
$('#question').focus();
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
});
|
|
@@ -170,6 +170,7 @@ function updateSendButtonState() {
|
|
|
170
170
|
* Auto-resizes the textarea to fit its content.
|
|
171
171
|
*/
|
|
172
172
|
function autoResizeTextarea(element) {
|
|
173
|
+
return;
|
|
173
174
|
element.style.height = 'auto';
|
|
174
175
|
element.style.height = (element.scrollHeight) + 'px';
|
|
175
176
|
}
|
|
@@ -273,7 +274,7 @@ const displayUserMessage = function(message, isEditable, originalQuestion) {
|
|
|
273
274
|
userMessage.append(messageText);
|
|
274
275
|
|
|
275
276
|
if (isEditable) {
|
|
276
|
-
const editIcon = $('<i>').addClass('p-2 bi bi-pencil-fill edit-icon').attr('title', 'Edit query').on('click', function () {
|
|
277
|
+
const editIcon = $('<i>').addClass('p-2 bi bi-pencil-fill edit-icon edit-pencil').attr('title', 'Edit query').on('click', function () {
|
|
277
278
|
$('#question').val(originalQuestion).focus();
|
|
278
279
|
autoResizeTextarea($('#question')[0]);
|
|
279
280
|
|
|
@@ -312,7 +313,7 @@ const showSpinner = function () {
|
|
|
312
313
|
const accessibilityClass = (typeof bootstrap !== 'undefined') ? 'visually-hidden' : 'sr-only';
|
|
313
314
|
const spinner = $(`
|
|
314
315
|
<div id="spinner" style="display: flex; align-items: center; justify-content: start; margin: 10px 0; padding: 10px;">
|
|
315
|
-
<div class="spinner-border
|
|
316
|
+
<div class="spinner-border" role="status" style="width: 1.5rem; height: 1.5rem; margin-right: 15px;">
|
|
316
317
|
<span class="${accessibilityClass}">Loading...</span>
|
|
317
318
|
</div>
|
|
318
319
|
<span style="font-weight: bold; font-size: 15px;">Cargando...</span>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
$(document).ready(function () {
|
|
2
|
+
$('#force-reload-button').on('click', function() {
|
|
3
|
+
reloadButton(this);
|
|
4
|
+
});
|
|
5
|
+
|
|
6
|
+
async function reloadButton(button) {
|
|
7
|
+
const originalIconClass = 'bi bi-arrow-clockwise';
|
|
8
|
+
const spinnerIconClass = 'spinner-border spinner-border-sm';
|
|
9
|
+
|
|
10
|
+
// Configuración de Toastr para que aparezca abajo a la derecha
|
|
11
|
+
toastr.options = {"positionClass": "toast-bottom-right", "preventDuplicates": true};
|
|
12
|
+
|
|
13
|
+
// 1. Deshabilitar y mostrar spinner
|
|
14
|
+
button.disabled = true;
|
|
15
|
+
const icon = button.querySelector('i');
|
|
16
|
+
icon.className = spinnerIconClass;
|
|
17
|
+
toastr.info('Iniciando recarga de contexto en segundo plano...');
|
|
18
|
+
|
|
19
|
+
// 2. prepare the api parameters
|
|
20
|
+
const apiPath = '/api/init-context';
|
|
21
|
+
const payload = {'user_identifier': window.user_identifier};
|
|
22
|
+
|
|
23
|
+
// 3. make the call to callToolkit
|
|
24
|
+
const data = await callToolkit(apiPath, payload, 'POST');
|
|
25
|
+
if (data) {
|
|
26
|
+
if (data.status === 'OK')
|
|
27
|
+
toastr.success(data.message || 'Contexto recargado.');
|
|
28
|
+
else
|
|
29
|
+
toastr.error(data.error_message || 'Ocurrió un error desconocido durante la recarga.');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
button.disabled = false;
|
|
33
|
+
icon.className = originalIconClass;
|
|
34
|
+
}
|
|
35
|
+
});
|