iatoolkit 0.8.1__py3-none-any.whl → 0.63.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 (159) hide show
  1. iatoolkit/__init__.py +8 -34
  2. iatoolkit/base_company.py +14 -3
  3. iatoolkit/common/routes.py +83 -52
  4. iatoolkit/common/session_manager.py +0 -1
  5. iatoolkit/common/util.py +0 -27
  6. iatoolkit/iatoolkit.py +61 -46
  7. iatoolkit/infra/llm_client.py +7 -8
  8. iatoolkit/infra/openai_adapter.py +1 -1
  9. iatoolkit/infra/redis_session_manager.py +48 -2
  10. iatoolkit/repositories/database_manager.py +17 -2
  11. iatoolkit/repositories/models.py +31 -6
  12. iatoolkit/repositories/profile_repo.py +7 -2
  13. iatoolkit/services/auth_service.py +188 -0
  14. iatoolkit/services/branding_service.py +147 -0
  15. iatoolkit/services/dispatcher_service.py +10 -40
  16. iatoolkit/services/excel_service.py +15 -15
  17. iatoolkit/services/history_service.py +3 -12
  18. iatoolkit/services/jwt_service.py +15 -24
  19. iatoolkit/services/onboarding_service.py +43 -0
  20. iatoolkit/services/profile_service.py +97 -44
  21. iatoolkit/services/query_service.py +124 -81
  22. iatoolkit/services/tasks_service.py +1 -1
  23. iatoolkit/services/user_feedback_service.py +67 -31
  24. iatoolkit/services/user_session_context_service.py +112 -54
  25. iatoolkit/static/images/fernando.jpeg +0 -0
  26. iatoolkit/static/js/{chat_feedback.js → chat_feedback_button.js} +6 -11
  27. iatoolkit/static/js/chat_history_button.js +126 -0
  28. iatoolkit/static/js/chat_logout_button.js +36 -0
  29. iatoolkit/static/js/chat_main.js +130 -220
  30. iatoolkit/static/js/chat_onboarding_button.js +97 -0
  31. iatoolkit/static/js/chat_prompt_manager.js +94 -0
  32. iatoolkit/static/js/chat_reload_button.js +52 -0
  33. iatoolkit/static/styles/chat_iatoolkit.css +329 -507
  34. iatoolkit/static/styles/chat_modal.css +95 -56
  35. iatoolkit/static/styles/landing_page.css +182 -0
  36. iatoolkit/static/styles/onboarding.css +169 -0
  37. iatoolkit/system_prompts/query_main.prompt +3 -12
  38. iatoolkit/templates/_company_header.html +20 -0
  39. iatoolkit/templates/_login_widget.html +40 -0
  40. iatoolkit/templates/base.html +8 -3
  41. iatoolkit/templates/change_password.html +54 -37
  42. iatoolkit/templates/chat.html +149 -66
  43. iatoolkit/templates/chat_modals.html +47 -18
  44. iatoolkit/templates/error.html +41 -8
  45. iatoolkit/templates/forgot_password.html +37 -24
  46. iatoolkit/templates/index.html +140 -0
  47. iatoolkit/templates/login_simulation.html +34 -0
  48. iatoolkit/templates/onboarding_shell.html +105 -0
  49. iatoolkit/templates/signup.html +64 -66
  50. iatoolkit/views/base_login_view.py +81 -0
  51. iatoolkit/views/change_password_view.py +23 -12
  52. iatoolkit/views/external_login_view.py +61 -28
  53. iatoolkit/views/{file_store_view.py → file_store_api_view.py} +9 -2
  54. iatoolkit/views/forgot_password_view.py +23 -13
  55. iatoolkit/views/history_api_view.py +52 -0
  56. iatoolkit/views/home_view.py +58 -25
  57. iatoolkit/views/index_view.py +14 -0
  58. iatoolkit/views/init_context_api_view.py +68 -0
  59. iatoolkit/views/llmquery_api_view.py +45 -0
  60. iatoolkit/views/login_simulation_view.py +81 -0
  61. iatoolkit/views/login_view.py +118 -34
  62. iatoolkit/views/logout_api_view.py +45 -0
  63. iatoolkit/views/{prompt_view.py → prompt_api_view.py} +7 -7
  64. iatoolkit/views/signup_view.py +38 -29
  65. iatoolkit/views/{tasks_view.py → tasks_api_view.py} +10 -36
  66. iatoolkit/views/tasks_review_api_view.py +55 -0
  67. iatoolkit/views/{user_feedback_view.py → user_feedback_api_view.py} +16 -31
  68. iatoolkit/views/verify_user_view.py +13 -8
  69. {iatoolkit-0.8.1.dist-info → iatoolkit-0.63.4.dist-info}/METADATA +2 -2
  70. iatoolkit-0.63.4.dist-info/RECORD +113 -0
  71. {iatoolkit-0.8.1.dist-info → iatoolkit-0.63.4.dist-info}/top_level.txt +0 -1
  72. iatoolkit/common/auth.py +0 -200
  73. iatoolkit/static/images/arrow_up.png +0 -0
  74. iatoolkit/static/images/diagrama_iatoolkit.jpg +0 -0
  75. iatoolkit/static/images/logo_clinica.png +0 -0
  76. iatoolkit/static/images/logo_iatoolkit.png +0 -0
  77. iatoolkit/static/images/logo_maxxa.png +0 -0
  78. iatoolkit/static/images/logo_notaria.png +0 -0
  79. iatoolkit/static/images/logo_tarjeta.png +0 -0
  80. iatoolkit/static/images/logo_umayor.png +0 -0
  81. iatoolkit/static/images/upload.png +0 -0
  82. iatoolkit/static/js/chat_history.js +0 -117
  83. iatoolkit/templates/home.html +0 -201
  84. iatoolkit/templates/login.html +0 -43
  85. iatoolkit/views/chat_token_request_view.py +0 -98
  86. iatoolkit/views/chat_view.py +0 -51
  87. iatoolkit/views/download_file_view.py +0 -58
  88. iatoolkit/views/external_chat_login_view.py +0 -88
  89. iatoolkit/views/history_view.py +0 -57
  90. iatoolkit/views/llmquery_view.py +0 -65
  91. iatoolkit/views/tasks_review_view.py +0 -83
  92. iatoolkit-0.8.1.dist-info/RECORD +0 -175
  93. tests/__init__.py +0 -5
  94. tests/common/__init__.py +0 -0
  95. tests/common/test_auth.py +0 -279
  96. tests/common/test_routes.py +0 -42
  97. tests/common/test_session_manager.py +0 -59
  98. tests/common/test_util.py +0 -444
  99. tests/companies/__init__.py +0 -5
  100. tests/conftest.py +0 -36
  101. tests/infra/__init__.py +0 -5
  102. tests/infra/connectors/__init__.py +0 -5
  103. tests/infra/connectors/test_google_drive_connector.py +0 -107
  104. tests/infra/connectors/test_local_file_connector.py +0 -85
  105. tests/infra/connectors/test_s3_connector.py +0 -95
  106. tests/infra/test_call_service.py +0 -92
  107. tests/infra/test_database_manager.py +0 -59
  108. tests/infra/test_gemini_adapter.py +0 -137
  109. tests/infra/test_google_chat_app.py +0 -68
  110. tests/infra/test_llm_client.py +0 -165
  111. tests/infra/test_llm_proxy.py +0 -122
  112. tests/infra/test_mail_app.py +0 -94
  113. tests/infra/test_openai_adapter.py +0 -105
  114. tests/infra/test_redis_session_manager_service.py +0 -117
  115. tests/repositories/__init__.py +0 -5
  116. tests/repositories/test_database_manager.py +0 -87
  117. tests/repositories/test_document_repo.py +0 -76
  118. tests/repositories/test_llm_query_repo.py +0 -340
  119. tests/repositories/test_models.py +0 -38
  120. tests/repositories/test_profile_repo.py +0 -142
  121. tests/repositories/test_tasks_repo.py +0 -76
  122. tests/repositories/test_vs_repo.py +0 -107
  123. tests/services/__init__.py +0 -5
  124. tests/services/test_dispatcher_service.py +0 -274
  125. tests/services/test_document_service.py +0 -181
  126. tests/services/test_excel_service.py +0 -208
  127. tests/services/test_file_processor_service.py +0 -121
  128. tests/services/test_history_service.py +0 -164
  129. tests/services/test_jwt_service.py +0 -255
  130. tests/services/test_load_documents_service.py +0 -112
  131. tests/services/test_mail_service.py +0 -70
  132. tests/services/test_profile_service.py +0 -379
  133. tests/services/test_prompt_manager_service.py +0 -190
  134. tests/services/test_query_service.py +0 -243
  135. tests/services/test_search_service.py +0 -39
  136. tests/services/test_sql_service.py +0 -160
  137. tests/services/test_tasks_service.py +0 -252
  138. tests/services/test_user_feedback_service.py +0 -389
  139. tests/services/test_user_session_context_service.py +0 -132
  140. tests/views/__init__.py +0 -5
  141. tests/views/test_change_password_view.py +0 -191
  142. tests/views/test_chat_token_request_view.py +0 -188
  143. tests/views/test_chat_view.py +0 -98
  144. tests/views/test_download_file_view.py +0 -149
  145. tests/views/test_external_chat_login_view.py +0 -120
  146. tests/views/test_external_login_view.py +0 -102
  147. tests/views/test_file_store_view.py +0 -128
  148. tests/views/test_forgot_password_view.py +0 -142
  149. tests/views/test_history_view.py +0 -336
  150. tests/views/test_home_view.py +0 -61
  151. tests/views/test_llm_query_view.py +0 -154
  152. tests/views/test_login_view.py +0 -114
  153. tests/views/test_prompt_view.py +0 -111
  154. tests/views/test_signup_view.py +0 -140
  155. tests/views/test_tasks_review_view.py +0 -104
  156. tests/views/test_tasks_view.py +0 -130
  157. tests/views/test_user_feedback_view.py +0 -214
  158. tests/views/test_verify_user_view.py +0 -110
  159. {iatoolkit-0.8.1.dist-info → iatoolkit-0.63.4.dist-info}/WHEEL +0 -0
@@ -0,0 +1,140 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}IAToolkit{% endblock %}
4
+
5
+ {% block styles %}
6
+ {# Enlazamos la hoja de estilos del website y los iconos de Bootstrap #}
7
+ <link rel="stylesheet" href="{{ url_for('static', filename='styles/landing_page.css') }}">
8
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
9
+ {% endblock %}
10
+
11
+
12
+ {% block content %}
13
+ <!-- 1. Encabezado con una clase 100% propia y única -->
14
+ <header class="website-header container">
15
+ <span class="website-brand">IAToolkit</span>
16
+ </header>
17
+
18
+ <!-- 2. Sección Principal (Hero) -->
19
+ <section class="hero-section">
20
+ <div class="container">
21
+ <div class="row align-items-center g-5 py-5">
22
+ <div class="col-lg-7">
23
+ <h1 class="hero-title gradient-text">Framework de IA Open Source</h1>
24
+ <ul class="hero-bullets mt-4">
25
+ <li><i class="bi bi-hdd-stack"></i>Integra tus bases de datos SQL y documentos para dar contexto real a tus asistentes.</li>
26
+ <li><i class="bi bi-code-slash"></i>Crea un repositorio de prompts validados por tu equipo para estandarizar y acelerar las tareas de IA.</li>
27
+ <li><i class="bi bi-shield-lock"></i> Monta la plataforma en tu propia infraestructura con control total sobre el LLM, garantizando la privacidad de los datos.</li> </ul>
28
+ </div>
29
+ <div class="col-lg-5">
30
+ <div class="border rounded-3 p-4 p-md-5 shadow-sm bg-light">
31
+ <h3 class="fw-bold text-center mb-3">Prueba la Plataforma</h3>
32
+ <p class="text-muted text-center mb-4">
33
+ Encontraras una demo de IAToolkit con una empresa ficticia que te permitira probar la plataforma.
34
+ </p>
35
+ <div class="d-grid">
36
+ {# Este botón usa la clase .btn-primary, que es estilizada por .hero-section .btn-primary en el CSS #}
37
+ <a href="{{ url_for('home', company_short_name='sample_company') }}"
38
+ class="btn btn-primary btn-lg fw-bold">
39
+ Acceder
40
+ </a>
41
+ </div>
42
+ </div>
43
+ </div>
44
+ </div>
45
+ </div>
46
+ </section>
47
+
48
+ <!-- 3. Sección de Características -->
49
+ <section class="features-section">
50
+ <div class="container">
51
+ <div class="row g-4">
52
+ <div class="col-lg-4 d-flex align-items-stretch">
53
+ <div class="opensource-box">
54
+ <div>
55
+ <div class="opensource-icon"><i class="bi bi-github"></i></div>
56
+ <h3>100% Open Source</h3>
57
+ <p>Construido en Python, IAToolkit te da control total. Audita el código, adáptalo y contribuye a una comunidad en crecimiento.</p>
58
+ </div>
59
+ <a href="https://github.com/flibedinsky/iatoolkit" target="_blank" class="btn btn-light mt-auto">
60
+ <i class="bi bi-github me-2"></i>Ir a GitHub
61
+ {% if iatoolkit_version %}
62
+ <span class="badge bg-dark ms-2">{{ iatoolkit_version }}</span>
63
+ {% endif %}
64
+ </a>
65
+ </div>
66
+ </div>
67
+ <div class="col-lg-4 d-flex align-items-stretch">
68
+ <div class="feature-item">
69
+ <div class="feature-icon"><i class="bi bi-hdd-stack"></i></div>
70
+ <h3>Conecta tus Datos</h3>
71
+ <p>Integra tus bases de datos SQL, documentos (PDFs, TXT) y otros sistemas para que el asistente tenga un contexto real de tu negocio.</p>
72
+ </div>
73
+ </div>
74
+ <div class="col-lg-4 d-flex align-items-stretch">
75
+ <div class="feature-item">
76
+ <div class="feature-icon"><i class="bi bi-gem"></i></div>
77
+ <h3>Multi-LLM</h3>
78
+ <p>No te ates a un solo proveedor. IAToolkit está diseñado para funcionar con diferentes modelos, incluyendo Gemini y OpenAI (GPT).</p>
79
+ </div>
80
+ </div>
81
+ </div>
82
+ <div class="row g-4 mt-4">
83
+ <div class="col-lg-4 d-flex align-items-stretch">
84
+ <div class="feature-item">
85
+ <div class="feature-icon"><i class="bi bi-magic"></i></div>
86
+ <h3>Prompt Manager</h3>
87
+ <p>Crea, gestiona y comparte una librería de prompts personalizados para tu empresa, optimizando las tareas repetitivas.</p>
88
+ </div>
89
+ </div>
90
+ <div class="col-lg-4 d-flex align-items-stretch">
91
+ <div class="feature-item">
92
+ <div class="feature-icon"><i class="bi bi-palette"></i></div>
93
+ <h3>100% Personalizable</h3>
94
+ <p>Adapta cada aspecto, desde la apariencia visual hasta las capacidades y herramientas del asistente, para que se alinee con la identidad de tu marca.</p>
95
+ </div>
96
+ </div>
97
+ <div class="col-lg-4 d-flex align-items-stretch">
98
+ <div class="feature-item">
99
+ <div class="feature-icon"><i class="bi bi-shield-lock"></i></div>
100
+ <h3>Privacidad Primero</h3>
101
+ <p>Despliega IAToolkit en tus propios servidores. Tus datos y consultas nunca son compartidos con terceros, garantizando la máxima confidencialidad.</p>
102
+ </div>
103
+ </div>
104
+ </div>
105
+ </div>
106
+ </section>
107
+
108
+ <!-- 3.5 Sobre el Autor -->
109
+ <section class="author-section py-5">
110
+ <div class="container">
111
+ <div class="author-card p-4">
112
+ <div class="row align-items-center g-4">
113
+ <div class="col-md-2 text-center">
114
+ <img src="{{ url_for('static', filename='images/fernando.jpeg') }}" alt="Foto de Fernando Libedinsky" class="img-fluid rounded-circle" style="max-width: 120px;">
115
+ </div>
116
+ <div class="col-md-10">
117
+ <div class="d-flex flex-wrap align-items-center mb-2">
118
+ <h5 class="mb-0 me-3">Sobre el autor</h5>
119
+ <a href="https://www.linkedin.com/in/fernandolibedinsky" target="_blank" rel="noopener" class="author-linkedin ms-auto">
120
+ <i class="bi bi-linkedin me-1"></i> LinkedIn
121
+ </a>
122
+ </div>
123
+ <p class="author-bio mb-0">
124
+ Soy <strong>Fernando Libedinsky</strong>, ingeniero de software y creador de <strong>IAToolkit</strong>.
125
+ <br>Tras una extensa trayectoria en el desarrollo de software, sigo movido por la misma curiosidad que me llevó a programar por primera vez: aprender, explorar y construir cosas nuevas.
126
+ <br>IAToolkit es la continuación de ese impulso, una plataforma creada para conectar rapidamente empresas con la IA.
127
+ </p>
128
+ </div>
129
+ </div>
130
+ </div>
131
+ </div>
132
+ </section>
133
+
134
+ <!-- 4. Footer -->
135
+ <footer class="landing-footer">
136
+ <div class="container">
137
+ &copy; 2024 IAToolkit - Proyecto Open Source
138
+ </div>
139
+ </footer>
140
+ {% endblock %}
@@ -0,0 +1,34 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}Prueba de Login para {{ company_short_name }}{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="container-fluid">
7
+ <div class="row flex-fill mt-5 justify-content-center">
8
+ <div class="col-12 col-lg-6">
9
+ <div class="border rounded p-4 p-md-5 shadow-sm bg-light">
10
+ <h3 class="text-muted fw-semibold text-start mb-3">
11
+ Login Externo para <span style="color:#0d6efd;">{{ company_short_name }}</span>
12
+ </h3>
13
+ <div class="text-center mb-4">
14
+ <p class="text-muted widget-intro-text">
15
+ Este formulario simula el inicio de una sesión externa. Al enviar, serás redirigido a la URL de login final.
16
+ </p>
17
+ </div>
18
+
19
+ <!-- Formulario HTML estándar que hace un POST a la misma URL -->
20
+ <form method="POST" action="">
21
+ <div class="mb-3">
22
+ <label for="external_user_id" class="form-label d-block">External user ID</label>
23
+ <input type="text" id="external_user_id" name="external_user_id" class="form-control" required>
24
+ </div>
25
+ <button type="submit" class="btn btn-primary">
26
+ Redirigir a External Login
27
+ </button>
28
+ </form>
29
+ </div>
30
+ </div>
31
+ </div>
32
+ </div>
33
+ {% endblock %}
34
+
@@ -0,0 +1,105 @@
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>
57
+ </div>
58
+ </div>
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>
64
+ </div>
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_button.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 }
100
+ });
101
+
102
+ onboarding.start();
103
+ })();
104
+ </script>
105
+ {% endblock %}
@@ -1,78 +1,76 @@
1
1
  {% extends "base.html" %}
2
2
 
3
- {% block title %}Registro de Usuario{% endblock %}
3
+ {% block title %}Crear Cuenta - {{ branding.name }}{% endblock %}
4
+
5
+ {% block styles %}
6
+ {# ¡Importante! Añadimos los estilos para el branding #}
7
+ <style>
8
+ {{ branding.css_variables | safe }}
9
+ </style>
10
+ <link rel="stylesheet" href="{{ url_for('static', filename='styles/chat_iatoolkit.css') }}">
11
+ {% endblock %}
4
12
 
5
13
  {% block content %}
14
+ {% include '_company_header.html' %}
6
15
 
7
16
 
8
- <div class="d-flex align-items-center p-3 border-bottom border-light-subtle">
9
- <div class="col-11 col-md-8 col-lg-5 p-3">
10
- <h4 class="text-muted fw-semibold mb-2 text-start">Crea tu cuenta</h4>
11
- <p class="text-muted mb-3 text-start" style="text-align: justify;">
12
- Regístrate para acceder a todas las funcionalidades de nuestra plataforma.
13
- </p>
14
- <form action="{{ url_for('signup', company_short_name=company_short_name) }}" method="post" >
15
- <div class="mb-3 ">
16
- <label for="email" class="form-label text-secondary">Correo Electrónico</label>
17
- <input type="email" autocomplete="off" id="email" name="email"
18
- class="form-control" required
19
- value="{{ form_data.email if form_data else '' }}">
20
- </div>
17
+ <!-- 3. Sección contenedora para centrar el contenido -->
18
+ <section class="hero-section">
19
+ <div class="container">
20
+ <div class="row justify-content-center">
21
+ <!-- Se ha reducido el ancho de la columna a lg-6 y md-8 -->
22
+ <div class="col-lg-6 col-md-8">
23
+ <div class="branded-form-container">
24
+ <h4 class="branded-form-title">Crea tu cuenta en {{ company.name }}</h4>
25
+ <form action="{{ url_for('signup', company_short_name=company_short_name) }}" method="post">
26
+ <div class="mb-3">
27
+ <label for="email" class="form-label text-secondary">Correo Electrónico</label>
28
+ <input type="email" autocomplete="off" id="email" name="email"
29
+ class="form-control" required
30
+ value="{{ form_data.email if form_data else '' }}">
31
+ </div>
21
32
 
22
- {% if is_mobile %}
23
- <!-- En móvil, cada input ocupa una fila -->
24
- <div class="mb-3">
25
- <label for="first_name" class="form-label text-secondary">Nombre</label>
26
- <input type="text" id="first_name" name="first_name"
27
- class="form-control" required
28
- value="{{ form_data.first_name if form_data else '' }}">
29
- </div>
30
- <div class="mb-3">
31
- <label for="last_name" class="form-label text-secondary">Apellido</label>
32
- <input type="text" id="last_name" name="last_name"
33
- class="form-control" required
34
- value="{{ form_data.last_name if form_data else '' }}">
35
- </div>
36
- {% else %}
37
- <div class="row">
38
- <!-- En escritorio, los inputs están en la misma fila -->
39
- <div class="col-md-6 mb-3">
40
- <label for="first_name" class="form-label text-secondary">Nombre</label>
41
- <input type="text" id="first_name" name="first_name"
42
- class="form-control" required
43
- value="{{ form_data.first_name if form_data else '' }}">
44
- </div>
45
- <div class="col-md-6 mb-3">
46
- <label for="last_name" class="form-label text-secondary">Apellido</label>
47
- <input type="text" id="last_name" name="last_name"
48
- class="form-control" required
49
- value="{{ form_data.last_name if form_data else '' }}">
50
- </div>
51
- </div>
33
+ <div class="row">
34
+ <div class="col-md-6 mb-3">
35
+ <label for="first_name" class="form-label text-secondary">Nombre</label>
36
+ <input type="text" id="first_name" name="first_name"
37
+ class="form-control" required
38
+ value="{{ form_data.first_name if form_data else '' }}">
39
+ </div>
40
+ <div class="col-md-6 mb-3">
41
+ <label for="last_name" class="form-label text-secondary">Apellido</label>
42
+ <input type="text" id="last_name" name="last_name"
43
+ class="form-control" required
44
+ value="{{ form_data.last_name if form_data else '' }}">
45
+ </div>
46
+ </div>
52
47
 
53
- {% endif %}
48
+ <div class="mb-3">
49
+ <label for="password" class="form-label text-secondary">Contraseña</label>
50
+ <input type="password" id="password" name="password" class="form-control" required>
51
+ <!-- Bloque de ayuda para la contraseña mejorado -->
52
+ <div class="d-flex align-items-start text-muted mt-2" style="font-size: 0.8rem;">
53
+ <i class="bi bi-info-circle me-2" style="font-size: 0.9rem; line-height: 1.4;"></i>
54
+ <span>Debe contener al menos 8 caracteres, mayúscula, minúscula, número y un carácter especial.</span>
55
+ </div>
56
+ </div>
54
57
 
55
- <div class="mb-3">
56
- <label for="password" class="form-label text-secondary">Contraseña</label>
57
- <input type="password" id="password" name="password" class="form-control" required>
58
- <small class="form-text text-muted">
59
- La contraseña debe contener al menos 8 caracteres, una letra mayúscula, una letra minúscula, un número y un carácter especial.
60
- </small>
58
+ <div class="mb-3">
59
+ <label for="confirm_password" class="form-label text-secondary">Confirmar Contraseña</label>
60
+ <input type="password" id="confirm_password" name="confirm_password" class="form-control" required>
61
+ </div>
61
62
 
63
+ <!-- Botón actualizado con la clase de branding -->
64
+ <button type="submit" class="btn btn-branded-primary w-100 fw-bold py-2 mt-3">Registrarse</button>
65
+ </form>
66
+ <!-- Nota de privacidad -->
67
+ <p class="text-muted small mb-0 text-center mt-4">
68
+ 🔒 Valoramos tu privacidad. Tus datos se usarán exclusivamente para el funcionamiento de la plataforma.
69
+ </p>
70
+ </div>
71
+ </div>
62
72
  </div>
63
- <div class="mb-3">
64
- <label for="confirm_password" class="form-label text-secondary">Confirmar Contraseña</label>
65
- <input type="password" id="confirm_password" name="confirm_password" class="form-control" required>
66
- </div>
67
- <button type="submit" class="btn btn-primary w-100">Registrarse</button>
68
- </form>
69
- <!-- Nota sobre privacidad -->
70
- <p class="text-muted small mb-0 text-start mt-3" style="text-align: justify;">
71
- 🔒 Valoramos tu privacidad. Tus datos serán utilizados exclusivamente para brindarte una mejor experiencia
72
- y no serán compartidos con terceros sin tu consentimiento. Para más información, consulta nuestra
73
- <a href="#" class="text-decoration-none fw-semibold">Política de Privacidad</a>.
74
- </p>
75
- </p>
76
- </div>
77
- </div>
73
+ </div>
74
+ </section>
75
+
78
76
  {% endblock %}
@@ -0,0 +1,81 @@
1
+ # iatoolkit/views/base_login_view.py
2
+ # Copyright (c) 2024 Fernando Libedinsky
3
+ # Product: IAToolkit
4
+ #
5
+ # IAToolkit is open source software.
6
+
7
+ from flask.views import MethodView
8
+ from flask import render_template, url_for
9
+ from injector import inject
10
+ from iatoolkit.services.profile_service import ProfileService
11
+ from iatoolkit.services.auth_service import AuthService
12
+ from iatoolkit.services.query_service import QueryService
13
+ from iatoolkit.services.branding_service import BrandingService
14
+ from iatoolkit.services.onboarding_service import OnboardingService
15
+ from iatoolkit.services.prompt_manager_service import PromptService
16
+ from iatoolkit.services.jwt_service import JWTService
17
+ from iatoolkit.repositories.models import Company
18
+
19
+
20
+ class BaseLoginView(MethodView):
21
+ """
22
+ Base class for views that initiate a session and decide the context
23
+ loading path (fast or slow).
24
+ """
25
+ @inject
26
+ def __init__(self,
27
+ profile_service: ProfileService,
28
+ auth_service: AuthService,
29
+ jwt_service: JWTService,
30
+ branding_service: BrandingService,
31
+ prompt_service: PromptService,
32
+ onboarding_service: OnboardingService,
33
+ query_service: QueryService
34
+ ):
35
+ self.profile_service = profile_service
36
+ self.auth_service = auth_service
37
+ self.jwt_service = jwt_service
38
+ self.branding_service = branding_service
39
+ self.prompt_service = prompt_service
40
+ self.onboarding_service = onboarding_service
41
+ self.query_service = query_service
42
+
43
+
44
+ def _handle_login_path(self,
45
+ company: Company,
46
+ user_identifier: str,
47
+ target_url: str,
48
+ redeem_token: str = None):
49
+ """
50
+ Centralized logic to decide between the fast path and the slow path.
51
+ """
52
+ # --- Get the company branding and onboarding_cards
53
+ branding_data = self.branding_service.get_company_branding(company)
54
+ onboarding_cards = self.onboarding_service.get_onboarding_cards(company)
55
+ company_short_name = company.short_name
56
+
57
+ # this service decides is the context needs to be rebuilt or not
58
+ prep_result = self.query_service.prepare_context(
59
+ company_short_name=company.short_name, user_identifier=user_identifier
60
+ )
61
+
62
+ if prep_result.get('rebuild_needed'):
63
+ # --- SLOW PATH: Render the loading shell ---
64
+ return render_template(
65
+ "onboarding_shell.html",
66
+ iframe_src_url=target_url,
67
+ branding=branding_data,
68
+ onboarding_cards=onboarding_cards
69
+ )
70
+ else:
71
+ # --- FAST PATH: Render the chat page directly ---
72
+ prompts = self.prompt_service.get_user_prompts(company_short_name)
73
+ return render_template(
74
+ "chat.html",
75
+ company_short_name=company_short_name,
76
+ user_identifier=user_identifier,
77
+ prompts=prompts,
78
+ branding=branding_data,
79
+ onboarding_cards=onboarding_cards,
80
+ redeem_token=redeem_token
81
+ )
@@ -4,8 +4,9 @@
4
4
  # IAToolkit is open source software.
5
5
 
6
6
  from flask.views import MethodView
7
- from flask import render_template, request
7
+ from flask import render_template, request, url_for, session, redirect
8
8
  from iatoolkit.services.profile_service import ProfileService
9
+ from iatoolkit.services.branding_service import BrandingService
9
10
  from itsdangerous import URLSafeTimedSerializer, SignatureExpired
10
11
  from flask_bcrypt import Bcrypt
11
12
  from injector import inject
@@ -14,8 +15,11 @@ import os
14
15
 
15
16
  class ChangePasswordView(MethodView):
16
17
  @inject
17
- def __init__(self, profile_service: ProfileService):
18
+ def __init__(self,
19
+ profile_service: ProfileService,
20
+ branding_service: BrandingService):
18
21
  self.profile_service = profile_service
22
+ self.branding_service = branding_service # 3. Guardar la instancia
19
23
 
20
24
  self.serializer = URLSafeTimedSerializer(os.getenv("PASS_RESET_KEY"))
21
25
  self.bcrypt = Bcrypt()
@@ -26,24 +30,31 @@ class ChangePasswordView(MethodView):
26
30
  if not company:
27
31
  return render_template('error.html', message=f"Empresa no encontrada: {company_short_name}"), 404
28
32
 
33
+ branding_data = self.branding_service.get_company_branding(company)
34
+
29
35
  try:
30
36
  # Decodificar el token
31
37
  email = self.serializer.loads(token, salt='password-reset', max_age=3600)
32
38
  except SignatureExpired as e:
33
39
  return render_template('forgot_password.html',
34
- alert_message="El enlace de cambio de contraseña ha expirado. Por favor, solicita uno nuevo.")
40
+ branding=branding_data,
41
+ alert_message="El enlace de cambio de contraseña ha expirado. Por favor, solicita uno nuevo.")
35
42
 
36
43
  return render_template('change_password.html',
37
44
  company_short_name=company_short_name,
38
45
  company=company,
46
+ branding=branding_data,
39
47
  token=token, email=email)
40
48
 
41
49
  def post(self, company_short_name: str, token: str):
42
50
  # get company info
43
51
  company = self.profile_service.get_company_by_short_name(company_short_name)
44
52
  if not company:
45
- return render_template('error.html', message=f"Empresa no encontrada: {company_short_name}"), 404
53
+ return render_template('error.html',
54
+ company_short_name=company_short_name,
55
+ message=f"Empresa no encontrada: {company_short_name}"), 404
46
56
 
57
+ branding_data = self.branding_service.get_company_branding(company)
47
58
  try:
48
59
  # Decodificar el token
49
60
  email = self.serializer.loads(token, salt='password-reset', max_age=3600)
@@ -51,6 +62,7 @@ class ChangePasswordView(MethodView):
51
62
  return render_template('forgot_password.html',
52
63
  company_short_name=company_short_name,
53
64
  company=company,
65
+ branding=branding_data,
54
66
  alert_message="El enlace de cambio de contraseña ha expirado. Por favor, solicita uno nuevo.")
55
67
 
56
68
  try:
@@ -72,20 +84,19 @@ class ChangePasswordView(MethodView):
72
84
  token=token,
73
85
  company_short_name=company_short_name,
74
86
  company=company,
87
+ branding=branding_data,
75
88
  form_data={"temp_code": temp_code,
76
89
  "new_password": new_password,
77
90
  "confirm_password": confirm_password},
78
91
  alert_message=response["error"]), 400
79
92
 
80
-
81
- return render_template('login.html',
82
- company_short_name=company_short_name,
83
- company=company,
84
- alert_icon='success',
85
- alert_message="Tu contraseña ha sido restablecida exitosamente. Ahora puedes iniciar sesión.")
93
+ # Éxito: Guardar mensaje en sesión y redirigir
94
+ session['alert_message'] = "Tu contraseña ha sido restablecida exitosamente. Ahora puedes iniciar sesión."
95
+ session['alert_icon'] = 'success'
96
+ return redirect(url_for('home', company_short_name=company_short_name))
86
97
 
87
98
  except Exception as e:
88
99
  return render_template("error.html",
89
- company=company,
90
100
  company_short_name=company_short_name,
91
- message="Ha ocurrido un error inesperado."), 500
101
+ branding=branding_data,
102
+ message=f"Ha ocurrido un error inesperado: {str(e)}"), 500