templates/_partials/courses/_js_modal_actions_empresa.html.twig line 1

Open in your IDE?
  1. <script>
  2. // Añadir clase logged-in al body si el usuario está logueado
  3. document.addEventListener('DOMContentLoaded', function() {
  4. {% if app.user %}
  5. document.body.classList.add('logged-in');
  6. {% endif %}
  7. // Configurar validación de fecha de inicio
  8. const fechaInicioInput = document.querySelector('input[name="solicitud_accion_formativa_search[fechaInicio]"]');
  9. if (fechaInicioInput) {
  10. // Establecer fecha mínima como hoy
  11. const today = new Date().toISOString().split('T')[0];
  12. fechaInicioInput.setAttribute('min', today);
  13. // Validación en tiempo real
  14. fechaInicioInput.addEventListener('change', function() {
  15. const selectedDate = new Date(this.value);
  16. const todayDate = new Date();
  17. todayDate.setHours(0, 0, 0, 0);
  18. if (selectedDate < todayDate) {
  19. this.setCustomValidity('{{ "validation.start_date_future"|trans({}, "courses") }}');
  20. this.reportValidity();
  21. } else {
  22. this.setCustomValidity('');
  23. }
  24. });
  25. }
  26. });
  27. document.addEventListener('DOMContentLoaded', function() {
  28. // Handle empresa course enrollment buttons
  29. {% if app.user and constant('App\\Enum\\RolEnum::EMPRESA') in app.user.roles %}
  30. const solicitarCursoButtons = document.querySelectorAll('.solicitar-curso-empresa');
  31. solicitarCursoButtons.forEach(function(button) {
  32. button.addEventListener('click', function(e) {
  33. e.preventDefault();
  34. mostrarModalSeleccionEmpleados(button, 'solicitarCurso');
  35. });
  36. });
  37. {% endif %}
  38. });
  39. /**
  40. * Show employee selection modal for company actions
  41. */
  42. function mostrarModalSeleccionEmpleados(button, accion) {
  43. const actividadId = button.dataset.actividadId;
  44. const actividadTipo = button.dataset.actividadTipo;
  45. const cursoNombre = button.dataset.cursoNombre || 'curso';
  46. const modalId = 'modalSeleccionEmpleados' + actividadTipo + actividadId + accion;
  47. let modal = document.getElementById(modalId);
  48. if (!modal) {
  49. // Create the modal
  50. modal = document.createElement('div');
  51. modal.id = modalId;
  52. modal.className = 'modal fade';
  53. modal.setAttribute('role', 'dialog');
  54. const tituloAccion = accion === 'mostrarInteres' ? '{{ "course.action.show_interest"|trans({}, "courses") }}' : '{{ "course.action.request_course"|trans({}, "courses") }}';
  55. modal.innerHTML = `
  56. <div class="modal-dialog modal-lg" role="document">
  57. <div class="modal-content">
  58. <div class="modal-header">
  59. <h5 class="modal-title">{{ "course.modal.select_employees"|trans({}, "courses") }} ${tituloAccion} "${cursoNombre}"</h5>
  60. <button type="button" class="close" data-dismiss="modal" aria-label="Close">
  61. <span aria-hidden="true">&times;</span>
  62. </button>
  63. </div>
  64. <div class="modal-body">
  65. <div id="empleados-loading-${modalId}" class="text-center">
  66. <i class="fa fa-spinner fa-spin"></i> {{ "course.modal.loading_employees"|trans({}, "courses") }}
  67. </div>
  68. <div id="empleados-error-${modalId}" style="display: none;" class="alert alert-danger">
  69. <!-- Error messages will be shown here -->
  70. </div>
  71. <div id="empleados-content-${modalId}" style="display: none;">
  72. <div class="form-group">
  73. <input type="text" class="form-control" id="buscar-empleado-${modalId}" placeholder="{{ "course.modal.search_placeholder"|trans({}, "courses") }}" style="max-width: 100%; width: 100%; box-sizing: border-box;">
  74. </div>
  75. <div class="empleados-list" id="empleados-list-${modalId}">
  76. <!-- Employee list will be populated here -->
  77. </div>
  78. </div>
  79. </div>
  80. <div class="modal-footer">
  81. <button type="button" class="btn btn-secondary" data-dismiss="modal">{{ "common.cancel"|trans }}</button>
  82. <button type="button" class="btn btn-primary" id="confirmar-accion-${modalId}" disabled>
  83. ${accion === 'mostrarInteres' ? '{{ "course.modal.update_interests"|trans({}, "courses") }}' : '{{ "course.modal.update_enrollments"|trans({}, "courses") }}'}
  84. </button>
  85. </div>
  86. </div>
  87. </div>
  88. `;
  89. document.body.appendChild(modal);
  90. // Add event listeners
  91. const confirmarBtn = modal.querySelector('#confirmar-accion-' + modalId);
  92. confirmarBtn.addEventListener('click', function() {
  93. confirmarAccionEmpresa(modalId, actividadId, actividadTipo, accion);
  94. });
  95. const searchInput = modal.querySelector('#buscar-empleado-' + modalId);
  96. searchInput.addEventListener('input', function() {
  97. filtrarEmpleados(modalId, this.value);
  98. });
  99. }
  100. // Show modal
  101. $(modal).modal('show');
  102. // Load employees
  103. cargarEmpleadosAutorizados(modalId, actividadId, actividadTipo, accion);
  104. }
  105. /**
  106. * Load authorized employees
  107. */
  108. function cargarEmpleadosAutorizados(modalId, actividadId, actividadTipo, accion) {
  109. const url = new URL('/es/empresa/mi-equipo/empleados-autorizados-cursos', window.location.origin);
  110. if (actividadId) url.searchParams.append('actividadId', actividadId);
  111. if (actividadTipo) url.searchParams.append('actividadTipo', actividadTipo);
  112. if (accion) url.searchParams.append('accion', accion);
  113. fetch(url, {
  114. method: 'GET',
  115. headers: {
  116. 'Content-Type': 'application/json',
  117. 'X-Requested-With': 'XMLHttpRequest'
  118. },
  119. credentials: 'same-origin'
  120. })
  121. .then(response => response.json())
  122. .then(data => {
  123. if (data.success) {
  124. mostrarListaEmpleados(modalId, data.empleados);
  125. } else {
  126. mostrarErrorEmpleados(modalId, data.message || '{{ "course.modal.error_loading_employees"|trans({}, "courses") }}');
  127. }
  128. })
  129. .catch(error => {
  130. console.error('Error:', error);
  131. mostrarErrorEmpleados(modalId, '{{ "course.modal.connection_error"|trans({}, "courses") }}');
  132. });
  133. }
  134. /**
  135. * Show employees list
  136. */
  137. function mostrarListaEmpleados(modalId, empleados) {
  138. const loadingDiv = document.getElementById('empleados-loading-' + modalId);
  139. const contentDiv = document.getElementById('empleados-content-' + modalId);
  140. const listDiv = document.getElementById('empleados-list-' + modalId);
  141. loadingDiv.style.display = 'none';
  142. contentDiv.style.display = 'block';
  143. if (empleados.length === 0) {
  144. listDiv.innerHTML = '<div class="alert alert-info">{{ "course.modal.no_authorized_employees"|trans({}, "courses") }}</div>';
  145. return;
  146. }
  147. let html = '<table class="table table-striped" style="margin-top: 15px;"><thead><tr>' +
  148. '<th><input type="checkbox" id="select-all-' + modalId + '"> {{ "course.modal.select_all"|trans({}, "courses") }}</th>' +
  149. '<th>{{ "course.modal.full_name"|trans({}, "courses") }}</th>' +
  150. '<th>{{ "course.modal.email"|trans({}, "courses") }}</th>' +
  151. '<th>{{ "course.modal.phone"|trans({}, "courses") }}</th>' +
  152. '</tr></thead><tbody>';
  153. empleados.forEach(function(empleado) {
  154. const isChecked = empleado.isPreseleccionado ? 'checked' : '';
  155. const initiallyChecked = empleado.isPreseleccionado ? 'true' : 'false';
  156. html += `
  157. <tr class="empleado-row" data-nombre="${empleado.nombreCompleto.toLowerCase()}" data-email="${empleado.email.toLowerCase()}">
  158. <td><input type="checkbox" class="empleado-checkbox" value="${empleado.id}" ${isChecked} data-initially-checked="${initiallyChecked}"></td>
  159. <td>${empleado.nombreCompleto}</td>
  160. <td>${empleado.email}</td>
  161. <td>${empleado.telefono || '-'}</td>
  162. </tr>
  163. `;
  164. });
  165. html += '</tbody></table>';
  166. listDiv.innerHTML = html;
  167. // Add event listeners
  168. const selectAllCheckbox = document.getElementById('select-all-' + modalId);
  169. const empleadoCheckboxes = listDiv.querySelectorAll('.empleado-checkbox');
  170. const confirmarBtn = document.getElementById('confirmar-accion-' + modalId);
  171. selectAllCheckbox.addEventListener('change', function() {
  172. empleadoCheckboxes.forEach(function(checkbox) {
  173. if (checkbox.closest('.empleado-row').style.display !== 'none') {
  174. checkbox.checked = selectAllCheckbox.checked;
  175. }
  176. });
  177. updateConfirmarButton(modalId);
  178. });
  179. /*empleadoCheckboxes.forEach(function(checkbox) {
  180. checkbox.addEventListener('change', function() {
  181. updateConfirmarButton(modalId);
  182. });
  183. });*/
  184. $(document).on("change", ".empleado-checkbox", function(event)
  185. {
  186. var $table = $(this).closest("table");
  187. var $checkboxes = $table.find("tbody .empleado-checkbox");
  188. var $headerCheckbox = $table.find("thead input[type='checkbox']");
  189. var allChecked = $checkboxes.length === $checkboxes.filter(":checked").length;
  190. $headerCheckbox.prop("checked", allChecked);
  191. updateConfirmarButton(modalId);
  192. });
  193. // Update button state initially (in case some employees are preselected)
  194. updateConfirmarButton(modalId);
  195. }
  196. /**
  197. * Show employees error
  198. */
  199. function mostrarErrorEmpleados(modalId, message) {
  200. const loadingDiv = document.getElementById('empleados-loading-' + modalId);
  201. const errorDiv = document.getElementById('empleados-error-' + modalId);
  202. loadingDiv.style.display = 'none';
  203. errorDiv.style.display = 'block';
  204. errorDiv.textContent = message;
  205. }
  206. /**
  207. * Filter employees
  208. */
  209. function filtrarEmpleados(modalId, searchTerm) {
  210. const listDiv = document.getElementById('empleados-list-' + modalId);
  211. const rows = listDiv.querySelectorAll('.empleado-row');
  212. searchTerm = searchTerm.toLowerCase();
  213. rows.forEach(function(row) {
  214. const nombre = row.dataset.nombre;
  215. const email = row.dataset.email;
  216. if (nombre.includes(searchTerm) || email.includes(searchTerm)) {
  217. row.style.display = '';
  218. } else {
  219. row.style.display = 'none';
  220. // Uncheck hidden rows
  221. const checkbox = row.querySelector('.empleado-checkbox');
  222. checkbox.checked = false;
  223. }
  224. });
  225. updateConfirmarButton(modalId);
  226. }
  227. /**
  228. * Update confirm button state
  229. */
  230. function updateConfirmarButton(modalId) {
  231. const listDiv = document.getElementById('empleados-list-' + modalId);
  232. const visibleCheckboxes = Array.from(listDiv.querySelectorAll('.empleado-checkbox')).filter(function(checkbox) {
  233. return checkbox.closest('.empleado-row').style.display !== 'none';
  234. });
  235. const confirmarBtn = document.getElementById('confirmar-accion-' + modalId);
  236. // Check if there's any change from the initial state (preselected employees)
  237. const initiallySelected = visibleCheckboxes.filter(function(checkbox) {
  238. return checkbox.dataset.initiallyChecked === 'true';
  239. });
  240. const currentlySelected = visibleCheckboxes.filter(function(checkbox) {
  241. return checkbox.checked;
  242. });
  243. // Enable button if there's any change from initial state
  244. const hasChanges = initiallySelected.length !== currentlySelected.length ||
  245. !initiallySelected.every(checkbox => checkbox.checked) ||
  246. !currentlySelected.every(checkbox => checkbox.dataset.initiallyChecked === 'true');
  247. confirmarBtn.disabled = !hasChanges;
  248. }
  249. /**
  250. * Confirm company action
  251. */
  252. function confirmarAccionEmpresa(modalId, actividadId, actividadTipo, accion) {
  253. const listDiv = document.getElementById('empleados-list-' + modalId);
  254. const checkedCheckboxes = listDiv.querySelectorAll('.empleado-checkbox:checked');
  255. const empleadosIds = Array.from(checkedCheckboxes).map(function(checkbox) {
  256. return parseInt(checkbox.value);
  257. });
  258. // Send AJAX request
  259. const url = '/es/courses/ajax/accion-curso-empresa';
  260. const requestData = {
  261. empleadosIds: empleadosIds,
  262. actividadId: actividadId,
  263. actividadTipo: actividadTipo,
  264. accion: accion
  265. };
  266. // Disable confirm button
  267. const confirmarBtn = document.getElementById('confirmar-accion-' + modalId);
  268. confirmarBtn.disabled = true;
  269. confirmarBtn.textContent = '{{ "course.modal.processing"|trans({}, "courses") }}';
  270. let modalTitle = $('#' + modalId + ' h5.modal-title').html();
  271. fetch(url, {
  272. method: 'POST',
  273. headers: {
  274. 'Content-Type': 'application/json',
  275. 'X-Requested-With': 'XMLHttpRequest'
  276. },
  277. body: JSON.stringify(requestData),
  278. credentials: 'same-origin'
  279. })
  280. .then(response => response.json())
  281. .then(data => {
  282. if (data.success) {console.log(data.details);
  283. if(data.details.inscritoAll)
  284. {
  285. if($("button[data-actividad-id=" + actividadId + "]").parent().hasClass('footer') || // LIST
  286. $("button[data-actividad-id=" + actividadId + "]").parent().hasClass('actions')) // DETALLE
  287. {
  288. $("button[data-actividad-id=" + actividadId + "]").parent().prepend('<span class="btn btn-secondary disabled">{{ 'courses.already_enrolled'|trans({}, 'courses')|default('Ya inscrito en este curso') }}</span>');
  289. $("button[data-actividad-id=" + actividadId + "]").remove();
  290. }
  291. }
  292. else if (data.details.interes || data.details.inscrito) {
  293. $("button[data-actividad-id=" + actividadId + "]").removeClass(["btn-secondary", "btn-primary"]).addClass("btn-primary");
  294. }
  295. else if(data.details.interes === false || data.details.inscrito === false)
  296. {
  297. $("button[data-actividad-id=" + actividadId + "]").removeClass(["btn-secondary", "btn-primary"]).addClass("btn-secondary");
  298. }
  299. var richMsg = data.message || '{{ "course.modal.action_completed"|trans({}, "courses") }}';
  300. if (data.breakdown) {
  301. var b = data.breakdown;
  302. var lines = [];
  303. if (b.added) lines.push('✓ ' + b.added + ' marcat' + (b.added > 1 ? 's' : '') + ' correctament');
  304. if (b.already) lines.push('• ' + b.already + ' ja ' + (b.already > 1 ? 'tenien' : 'tenia') + ' interès');
  305. if (b.no_account) lines.push('✕ ' + b.no_account + ' sense compte (no registrats al sistema)');
  306. if (b.errors && b.errors.length) {
  307. b.errors.forEach(function(e) { lines.push('✕ ' + e); });
  308. }
  309. if (lines.length) richMsg = lines.join('<br>');
  310. }
  311. AlertUtils.success(modalTitle, richMsg, {html: true}, AlertUtils.Buttons.simple);
  312. // Close modal
  313. $('#' + modalId).modal('hide');
  314. $("#" + modalId + " .empleados-list").html(null);
  315. $("#" + modalId + " div.alert").html(null);
  316. // Show success message
  317. {#alert(data.message || '{{ "course.modal.action_completed"|trans({}, "courses") }}');#}
  318. // Optionally reload page or update UI
  319. // window.location.reload();
  320. } else {
  321. {#alert(data.message || '{{ "course.modal.error_processing_action"|trans({}, "courses") }}');#}
  322. AlertUtils.danger(modalTitle, data.message || '{{ "course.modal.error_processing_action"|trans({}, "courses") }}', {}, AlertUtils.Buttons.simple);
  323. }
  324. })
  325. .catch(error => {
  326. console.error('Error:', error);
  327. {#alert('{{ "course.modal.connection_error_processing"|trans({}, "courses") }}');#}
  328. AlertUtils.danger(modalTitle, data.message || '{{ "course.modal.connection_error_processing"|trans({}, "courses") }}', {}, AlertUtils.Buttons.simple);
  329. })
  330. .finally(function() {
  331. // Re-enable confirm button
  332. confirmarBtn.disabled = false;
  333. confirmarBtn.textContent = accion === 'mostrarInteres' ? '{{ "course.modal.update_interests"|trans({}, "courses") }}' : '{{ "course.modal.update_enrollments"|trans({}, "courses") }}';
  334. });
  335. }
  336. </script>