dashboardUniversitat.html 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080
  1. <!DOCTYPE html>
  2. <html xmlns="http://www.w3.org/1999/xhtml"
  3. xmlns:th="http://www.thymeleaf.org">
  4. <head th:replace="~{layouts/common.html :: head}"></head>
  5. <link th:href="@{/js/gijgo/css/gijgo.min.css}" rel="stylesheet"/>
  6. <link th:href="@{/js/jquery/datatables.min.css}" rel="stylesheet"/>
  7. <link th:href="@{/js/frappe-gantt/frappe-gantt.min.css}" rel="stylesheet"/>
  8. <style>
  9. table.tableizer-table {
  10. font-size: 12px;
  11. border: 1px solid #CCC;
  12. font-family: Arial, Helvetica, sans-serif;
  13. }
  14. .tableizer-table td {
  15. padding: 4px;
  16. margin: 3px;
  17. border: 1px solid #CCC;
  18. }
  19. .tableizer-table th {
  20. background-color: #1B3552;
  21. color: #FFF;
  22. font-weight: bold;
  23. }
  24. .chart-container{
  25. min-height:150px;
  26. }
  27. .gantt-container{
  28. height: calc(100vh - 250px);
  29. }
  30. .dropdown-colvis ~ .dropdown-menu {
  31. transform: translate(-100px, 0px);
  32. }
  33. </style>
  34. <body id="page-top">
  35. <!-- Navigation -->
  36. <nav th:replace="~{layouts/common.html :: navbar}"></nav>
  37. <!-- Content -->
  38. <div class="content uv-content d-flex" id="uv-wrapper">
  39. <div class="bg-light border-right" id="uv-sidebar-wrapper" th:replace="~{layouts/sidebar.html :: sidebar}"></div>
  40. <div class="container uv-home-section navVisible" id="uv-content-wrapper">
  41. <div class="uv-loading-spinner" th:text="#{global.loading}"></div>
  42. <div class="col-sm-12"></div>
  43. <div class="uv-container">
  44. <div class="row">
  45. <div class="col-sm-10">
  46. <h5 style="margin-top:10px;"><span style="text-transform: uppercase;" th:text="${#locale.language} == 'es' ? ${organ.nomCas}:${organ.nomVal}">nombre centro</span></h5>
  47. </div>
  48. <div class="col-sm-2" style="padding:0;">
  49. <img th:src="@{${'/logos/C' + organ.lugar +'.png'}}" class="float-right img-responsive img-rounded" style="width:80px;margin-top:0px;">
  50. </div>
  51. </div>
  52. <div class="clearfix"></div>
  53. <div class="row" style="margin-top:5px;">
  54. <div class="card card-default card-body" style="padding: 0 !important;">
  55. <ul id="tabsJustified" class="nav nav-tabs nav-justified">
  56. <li class="nav-item">
  57. <a class="nav-link active" href="" data-target="#tab3" data-toggle="tab" th:text="#{dashboard.menu.tits}" onclick="window.location.hash = 'tab3'">Titulaciones</a>
  58. </li>
  59. <li class="nav-item">
  60. <a class="nav-link" href="" data-target="#tab0" data-toggle="tab" th:text="#{dashboard.menu.summary}" onclick="window.location.hash = 'tab0'">Resumen</a>
  61. </li>
  62. <li class="nav-item">
  63. <a class="nav-link" href="" data-target="#tab1" data-toggle="tab" th:text="#{dashboard.menu.procedures}" onclick="window.location.hash = 'tab1'">Procedimientos SAIC</a>
  64. </li>
  65. <li class="nav-item">
  66. <a class="nav-link" href="" data-target="#tab2" data-toggle="tab" th:text="#{dashboard.menu.docs}" onclick="window.location.hash = 'tab2'">Seguimiento/Acreditación</a>
  67. </li>
  68. <li class="nav-item">
  69. <a class="nav-link" href="" data-target="#tab4" data-toggle="tab" th:text="#{dashboard.menu.managers}" onclick="window.location.hash = 'tab4'">Responsables</a>
  70. </li>
  71. </ul>
  72. <!--/tabs-->
  73. <br>
  74. <div id="viewTabsProcYears" class="tab-content">
  75. <div class="tab-pane" id="tab0">
  76. <div class="row">
  77. <div class="col-lg-12" style="margin-top:-10px;margin-bottom:5px;padding-left:30px;padding-right:30px;">
  78. <span class="gantt-legend-done"><span th:text="#{dashboard.summary.legend.active}">Finalizado</span></span>
  79. <span class="gantt-legend-active"><span th:text="#{dashboard.summary.legend.ontime}">En curso</span></span>
  80. <span class="gantt-legend-expired"><span th:text="#{dashboard.summary.legend.delayed}">Atrasado</span></span>
  81. <span class="gantt-legend-closed"><span th:text="#{dashboard.summary.legend.closed}">Cerrado</span></span>
  82. <div class="input-group" style="text-align:right;float:right;margin-right:0px;width:auto !important;">
  83. <div class="input-group-prepend">
  84. <span class="input-group-text"><span th:text="#{dashboard.gantt.selector}">Procedimientos de</span></span>
  85. </div>
  86. <select class="form-control pointer" id="ganttOrigin" style="max-width:300px;" onchange="changeGanttOrigin();">
  87. <option th:each="item : ${titulacions}" th:text="${#locale.language} == 'es' ? ${item.nomCas}:${item.nomVal}" th:attr="value=${item.ruct}">Tit 1</option>
  88. </select>
  89. </div>
  90. </div>
  91. <div class="col-lg-12" style="position:relative;z-index:0;">
  92. <svg id="gantt"></svg>
  93. </div>
  94. </div>
  95. </div>
  96. <div class="tab-pane" id="tab1">
  97. <div class="col-lg-12" style="margin-top:-10px;margin-bottom:20px;">
  98. <span class="gantt-legend-done"><span th:text="#{dashboard.summary.legend.active}">Finalizado</span></span>
  99. <span class="gantt-legend-active"><span th:text="#{dashboard.summary.legend.ontime}">En curso</span></span>
  100. <span class="gantt-legend-closed"><span th:text="#{dashboard.summary.legend.closed}">Cerrado</span></span>
  101. <div class="input-group" style="text-align:right;float:right;margin-right:0px;width:auto !important;">
  102. <div class="input-group-prepend">
  103. <span class="input-group-text">Procedimientos de:</span>
  104. </div>
  105. <select class="form-control pointer" id="reportsOrigin" style="max-width:300px;" onchange="changeReportsOrigin();">
  106. <option selected th:value="${organ.ruct}" th:text="#{dashboard.data.legend.2}">Centre</option>
  107. <option th:each="item : ${titulacions}" th:text="${#locale.language} == 'es' ? ${item.nomCas}:${item.nomVal}" th:attr="value=${item.ruct}">Tit 1</option>
  108. </select>
  109. </div>
  110. </div>
  111. <div class="col-lg-12">
  112. <div id="treeProcedures"></div>
  113. </div>
  114. </div>
  115. <div class="tab-pane" id="tab2">
  116. <span th:if="${editable}" class="btn btn-primary pointer" style="z-index:100;float:right;margin-right:25px;font-size:75%;padding: 5px 8px 3px 8px;" onclick="$('#newDocumentModal').modal('toggle');"><i class="fa fa-plus"></i></span>
  117. <span th:if="${editable}" class="btn btn-warning pointer" style="z-index:100;float:right;margin-right:10px;font-size:75%;padding: 5px 8px 3px 8px;" onclick="$('#hideDocumentsModal').modal('toggle');"><i class="fas fa-eye-slash"></i></span>
  118. <div class="row" style="padding-left:25px;width:100%;">
  119. <div class="col-lg-12" style="margin-left:-5px;">
  120. <strong th:text="#{dashboard.acred.docs.title}">Documentación</strong>
  121. <div id="treeDocuments"></div>
  122. </div>
  123. </div>
  124. </div>
  125. <div class="tab-pane active" id="tab3">
  126. <div class="col-lg-12" style="margin-left:0px;margin-top:-15px;width:100%;position:relative;z-index:0;">
  127. <table id="titsTable" class="table table-striped table-bordered display responsive no-wrap" style="width:100%;position:relative;z-index:0;">
  128. <thead>
  129. <tr>
  130. <th class="uv-table" th:text="#{dashboard.tits.ruct}">RUCT</th>
  131. <th class="uv-table" th:text="#{dashboard.tits.codes}">Códigos</th>
  132. <th class="uv-table" th:text="#{dashboard.tits.centre}">Centro</th>
  133. <th class="uv-table" th:text="#{dashboard.tits.tit}">Titulación</th>
  134. <th class="uv-table" th:text="#{dashboard.tits.type}">Tipo</th>
  135. <th class="uv-table" th:text="#{dashboard.acred.table.next}">Próx Accr.</th>
  136. <th class="uv-table" th:text="#{dashboard.acred.table.acred}">Fecha Accr.</th>
  137. <th class="uv-table" th:text="#{dashboard.acred.table.renov}">Renovación</th>
  138. <th class="uv-table" th:text="#{dashboard.acred.table.segui}">Seguimiento</th>
  139. <th class="uv-table" th:text="#{dashboard.acred.table.verif}">Verificación</th>
  140. <th class="uv-table" th:text="#{dashboard.acred.table.impla}">Implantación</th>
  141. <th class="uv-table" th:text="#{dashboard.acred.table.inter}">InterUniv.</th>
  142. </tr>
  143. </thead>
  144. <tbody>
  145. <tr th:each="item,inRowID : ${titulacions}">
  146. <td>
  147. <span th:text="${item.ruct}"></span>
  148. </td>
  149. <td>
  150. <span th:text="${item.codis2}"></span>
  151. </td>
  152. <td>
  153. <a target="_blank" th:href="${'/dashboard/'+item.ruct2}"><span th:text="${#locale.language} == 'es' ? ${item.nomCas2}:${item.nomVal2}"></span></a>
  154. </td>
  155. <td>
  156. <a target="_blank" th:href="${'/dashboard/'+item.ruct}"><span th:text="${#locale.language} == 'es' ? ${item.nomCas}:${item.nomVal}"></span></a>
  157. </td>
  158. <td>
  159. <span th:text="${item.tambit == 'M' ? 'Máster' : item.tambit == 'G' ? 'Grado' : 'Doctorado'}"></span>
  160. </td>
  161. <td>
  162. <span th:text="${item.acreditacio?.grupCurs+' - '+item.acreditacio?.grup}"></span>
  163. </td>
  164. <td>
  165. <span th:text="${#temporals.format(item.acreditacio?.dataAcred, 'dd/MM/yyyy')}"></span>
  166. </td>
  167. <td>
  168. <span th:text="${#temporals.format(item.acreditacio?.dataRenov, 'dd/MM/yyyy')}"></span>
  169. </td>
  170. <td>
  171. <span th:text="${#temporals.format(item.acreditacio?.dataSegui, 'dd/MM/yyyy')}"></span>
  172. </td>
  173. <td>
  174. <span th:text="${#temporals.format(item.acreditacio?.dataVerif, 'dd/MM/yyyy')}"></span>
  175. </td>
  176. <td>
  177. <span th:text="${item.acreditacio?.cursImpla}"></span>
  178. </td>
  179. <td>
  180. <span th:if="${item.acreditacio != null}" th:text="${item.acreditacio?.inter ? 'SI' : 'NO'}"></span>
  181. </td>
  182. </tr>
  183. </tbody>
  184. </table>
  185. </div>
  186. </div>
  187. <div class="tab-pane" id="tab4">
  188. <span class="btn btn-primary pointer" id="btnAddManager" style="z-index:100;float:right;margin-right:25px;font-size:75%;padding: 5px 8px 3px 8px;" data-toggle="modal" data-target="#newRoleModal"><i class="fa fa-plus"></i></span>
  189. <div class="uv-table-group" th:if="${results}" style="cursor: auto; width: 90%;">
  190. <div class="col-sm-12 uv-table-section" th:each="item : ${resp_titulacions}" style="margin-top:20px;">
  191. <div class="col-sm-12 uv-table-section" th:each="item : ${resp_titulacions}" style="margin-top:20px;">
  192. <strong><span th:text="${#locale.language} == 'es' ? ${item.rol.descripcioCas}:${item.rol.descripcioVal}"></span>: </strong>
  193. <a th:href="'mailto:'+${item.usuari.email}" th:title="${item.usuari.email}" th:text="${item.usuari.nom + ' ' + item.usuari.cognoms}"></a> (<small><span th:text="#{managers.since}">Desde el</span> <span th:text="${item.inici}"></span></small>)
  194. </div>
  195. <strong><span th:text="${#locale.language} == 'es' ? ${item.rol.descripcioCas}:${item.rol.descripcioVal}"></span>: </strong>
  196. <a th:href="'mailto:'+${item.usuari.email}" th:title="${item.usuari.email}" th:text="${item.usuari.nom + ' ' + item.usuari.cognoms}"></a> (<small><span th:text="#{managers.since}">Desde el</span> <span th:text="${item.inici}"></span></small>)
  197. </div>
  198. <div class="col-sm-12 uv-table-section" th:each="item : ${resp_centres}" style="display: flex; align-items: flex-start; margin-bottom: 15px;">
  199. <form id="deleteUserForm" enctype='multipart/form-data' onsubmit="deleteUserRole(event)" style="margin-right: 15px;">
  200. <div class="form-group">
  201. <input type="hidden" name="idRol" th:value="${item.rol.idRol}">
  202. <input type="hidden" name="usuari" th:value="${item.usuari.usuari}">
  203. <input type="hidden" name="tlugar" th:value="${item.organ.tlugar}">
  204. <input type="hidden" name="lugar" th:value="${item.organ.lugar}">
  205. <button class="btn" style="width:40px; height:38px; color: red;" th:title="#{admin.action.delete}" >
  206. <i class="fas fa-times"></i>
  207. </button>
  208. </div>
  209. </form>
  210. <div>
  211. <strong th:text="${#locale.language} == 'es' ? ${item.rol.descripcioCas}:${item.rol.descripcioVal}"></strong>
  212. <br>
  213. <a th:href="'mailto:'+${item.usuari.email}" th:title="${item.usuari.email}" th:text="${item.usuari.nom + ' ' + item.usuari.cognoms}"></a>
  214. (<small><span th:text="#{managers.since}">Desde el</span> <span th:text="${item.inici}"></span></small>)
  215. </div>
  216. </div>
  217. </div>
  218. </div>
  219. </div>
  220. <!--/tabs content-->
  221. </div><!--/card-->
  222. </div><!--/col-->
  223. </div>
  224. </div>
  225. </div>
  226. <div th:if="${editable}" class="modal fade" id="newDocumentModal" tabindex="-1" role="dialog" aria-labelledby="newDocumentModal" aria-hidden="true">
  227. <div class="modal-dialog modal-lg">
  228. <div class="modal-content">
  229. <div class="modal-header">
  230. <h5 class="modal-title">Añadir documento</h5>
  231. <button type="button" class="close" data-dismiss="modal" aria-label="Close">
  232. <span aria-hidden="true">&times;</span>
  233. </button>
  234. </div>
  235. <div class="modal-body">
  236. <div class="container-fluid">
  237. <form id="newDocumentForm" style="margin-top:25px;">
  238. <input type="hidden" id="fileLugar" name="lugar" th:value="${organ.lugar}">
  239. <input type="hidden" id="fileTlugar" name="tlugar" th:value="${organ.tlugar}">
  240. <div class="uv-table-group-procedure" id="categoryContainer">
  241. <label>Categoría</label>
  242. <select class="form-control selectpicker" id="categorySelector" name="category" data-width="100%" data-actions-box="true" th:attr="data-none-selected-text=#{global.selectors.noData}" required>
  243. <option th:each="item : ${categories}" th:text="${#locale.language} == 'es' ? ${item.nomCas}:${item.nomVal}" th:attr="value=${item.idCategoria}">
  244. </select>
  245. </div>
  246. <div class="uv-table-group-procedure documentSelectorContainer">
  247. <label>Documento</label>
  248. <select class="form-control selectpicker" id="documentSelector" name="idCategoria" data-showContent="true" data-width="100%" data-actions-box="true" th:attr="data-none-selected-text=#{global.selectors.noData}" required>
  249. </select>
  250. </div>
  251. <div class="uv-table-group-procedure documentSelectorContainer">
  252. <br>
  253. <div class="custom-file">
  254. <input type="file" class="custom-file-input pointer" id="customFile" name="file" required>
  255. <label class="custom-file-label pointer" for="customFile">Seleccionar archivo</label>
  256. </div>
  257. </div>
  258. </form>
  259. </div>
  260. </div>
  261. <div class="modal-footer">
  262. <button type="button" class="btn btn-secondary" data-dismiss="modal" th:text="#{global.cancel}">Cancelar</button>
  263. <button class="btn btn-success" type="submit" form="newDocumentForm" th:text="#{global.confirm}">Confirmar</button>
  264. </div>
  265. </div>
  266. </div>
  267. </div>
  268. <div th:if="${editable}" class="modal fade" id="hideDocumentsModal" tabindex="-1" role="dialog" aria-labelledby="hideDocumentsModal" aria-hidden="true">
  269. <div class="modal-dialog modal-lg">
  270. <div class="modal-content">
  271. <div class="modal-header">
  272. <h5 class="modal-title">Ocultar todos los documentos visibles</h5>
  273. <button type="button" class="close" data-dismiss="modal" aria-label="Close">
  274. <span aria-hidden="true">&times;</span>
  275. </button>
  276. </div>
  277. <div class="modal-body">
  278. <div class="container-fluid">
  279. <p>Se ocultarán todos los documentos visibles.</p>
  280. </div>
  281. </div>
  282. <div class="modal-footer">
  283. <button type="button" class="btn btn-secondary" data-dismiss="modal" th:text="#{global.cancel}">Cancelar</button>
  284. <button class="btn btn-success" th:text="#{global.confirm}" onclick="hideAllDocuments();">Confirmar</button>
  285. </div>
  286. </div>
  287. </div>
  288. </div>
  289. <div th:if="${editable}" class="modal fade" id="newOrganModal" tabindex="-1" role="dialog" aria-labelledby="newOrganModal" aria-hidden="true">
  290. <div class="modal-dialog modal-lg">
  291. <div class="modal-content">
  292. <div class="modal-header">
  293. <h5 class="modal-title">Añadir organo</h5>
  294. <button type="button" class="close" data-dismiss="modal" aria-label="Close">
  295. <span aria-hidden="true">&times;</span>
  296. </button>
  297. </div>
  298. <div class="modal-body">
  299. <div class="container-fluid">
  300. <form id="newOrganForm">
  301. <div class="uv-table-group-procedure mb-4">
  302. <label>Organo a añadir</label>
  303. <select class="form-control selectpicker" id="typeSelector" onchange="toggleFormFields(this.value)">
  304. <option value="centro">Centro</option>
  305. <option value="titulacion">Titulación</option>
  306. </select>
  307. </div>
  308. <hr>
  309. <div id="sectionCentro">
  310. <div class="uv-table-group-procedure">
  311. <label>Codigo centro</label>
  312. <input type="number" class="form-control" name="codiCentro">
  313. </div>
  314. <div class="uv-table-group-procedure">
  315. <label>Nombre Castellano</label>
  316. <input type="text" class="form-control" name="nomCasCentro">
  317. </div>
  318. <div class="uv-table-group-procedure">
  319. <label>Nombre Valenciano</label>
  320. <input type="text" class="form-control" name="nomValCentro">
  321. </div>
  322. <div class="uv-table-group-procedure">
  323. <label>RUCT</label>
  324. <input type="number" class="form-control" name="ructCentro">
  325. </div>
  326. </div>
  327. <div id="sectionTitulacion" style="display:none;">
  328. <div class="uv-table-group-procedure">
  329. <label>Codigo titulación</label>
  330. <input type="number" class="form-control" name="codiTit">
  331. </div>
  332. <div class="uv-table-group-procedure">
  333. <label>Centro perteneciente</label>
  334. <select class="selectpicker" id="roleCentersSelector" name="centre" data-live-search="true" data-width="100%" data-actions-box="true" required>
  335. <option th:each="item : ${centers}" th:text="${#locale.language} == 'es' ? ${item.nomCas}:${item.nomVal}" th:attr="value=${item.lugar}"></option>
  336. </select>
  337. </div>
  338. <div class="uv-table-group-procedure">
  339. <label>Nombre Castellano</label>
  340. <input type="text" class="form-control" name="nomCasTit">
  341. </div>
  342. <div class="uv-table-group-procedure">
  343. <label>Nombre Valenciano</label>
  344. <input type="text" class="form-control" name="nomValTit">
  345. </div>
  346. <div class="uv-table-group-procedure">
  347. <label>RUCT</label>
  348. <input type="number" class="form-control" name="ructTit">
  349. </div>
  350. <div class="uv-table-group-procedure">
  351. <label>Ámbito</label>
  352. <select class="form-control selectpicker" name="tambit" data-width="100%">
  353. <option value="G">Grado</option>
  354. <option value="M">Máster</option>
  355. <option value="D">Doctorado</option>
  356. </select>
  357. </div>
  358. </div>
  359. </form>
  360. </div>
  361. </div>
  362. <div class="modal-footer">
  363. <button type="button" class="btn btn-secondary" data-dismiss="modal" th:text="#{global.cancel}">Cancelar</button>
  364. <button class="btn btn-success" type="submit" form="newOrganForm" th:text="#{global.confirm}">Confirmar</button>
  365. </div>
  366. </div>
  367. </div>
  368. </div>
  369. <div class="modal fade" id="newRoleModal" tabindex="-1" role="dialog" aria-labelledby="newRoleModal" aria-hidden="true" th:if="${#authentication.principal.isAdmin() or #authentication.principal.isGranted()}">
  370. <div class="modal-dialog modal-lg">
  371. <div class="modal-content">
  372. <div class="modal-header">
  373. <h5 class="modal-title" th:text="#{admin.managers.newRole}">Añadir responsable</h5>
  374. <button type="button" class="close" data-dismiss="modal" aria-label="Close">
  375. <span aria-hidden="true">&times;</span>
  376. </button>
  377. </div>
  378. <div class="modal-body">
  379. <div class="container-fluid">
  380. <form id="newRoleForm" enctype='multipart/form-data' onsubmit="addUserRole(event)" style="margin-top:25px;">
  381. <input type="hidden" name="centre" th:value="${organ.lugar2}">
  382. <input type="hidden" name="titulacio" th:value="${organ.lugar}">
  383. <div class="form-group">
  384. <label th:text="#{admin.managers.user}">Usuario</label>&nbsp;&nbsp;&nbsp;<span class="pointer" style="color:#007bff;" onclick="newUser();" th:title="#{admin.managers.newUser}"><i id="addUserIcon" class="fas fa-plus"></i></span>
  385. <div id="userSelectorContainer">
  386. <select class="selectpicker" id="userSelector" name="usuari" data-dropup-auto="false" data-live-search="true" data-width="100%" data-actions-box="true" required>
  387. <option th:each="item : ${users}" th:text="${item.nom}+' '+${item.cognoms}+' ('+${item.email}+')'" th:attr="value=${item.usuari}"></option>
  388. </select>
  389. </div>
  390. <div class="form-group" id="newUserContainer">
  391. <div class="row">
  392. <div class="form-group col-6">
  393. <label th:text="#{admin.managers.firstname}">Nombre</label>
  394. <input class="form-control" type="text" value="" name="firstname" id="firstname">
  395. </div>
  396. <div class="form-group col-6">
  397. <label th:text="#{admin.managers.lastname}">Apellidos</label>
  398. <input class="form-control" type="text" value="" name="lastname" id="lastname">
  399. </div>
  400. <div class="form-group col-6">
  401. <label th:text="#{admin.managers.user}">Usuario</label>
  402. <input class="form-control" type="text" value="" name="username" id="username" onkeyup="setEmail();">
  403. </div>
  404. <div class="form-group col-6">
  405. <label th:text="#{admin.managers.email}">Email</label>
  406. <input class="form-control" type="email" value="" name="email" id="email">
  407. </div>
  408. </div>
  409. </div>
  410. </div>
  411. <div class="form-group">
  412. <label th:text="#{admin.managers.role}">Rol</label>
  413. <select class="selectpicker" id="roleSelector" name="idRol" data-dropup-auto="false" data-live-search="true" data-width="100%" data-actions-box="true" th:attr="data-none-selected-text=#{global.selectors.noData}" required>
  414. <option th:each="item : ${roles}" th:text="${#locale.language} == 'es' ? ${item.descripcioCas}:${item.descripcioVal}" th:attr="value=${item.idRol}"></option>
  415. </select>
  416. </div>
  417. </form>
  418. </div>
  419. </div>
  420. <div class="modal-footer">
  421. <button type="button" class="btn btn-secondary" data-dismiss="modal" th:text="#{global.cancel}">Cancelar</button>
  422. <button class="btn btn-success" type="submit" form="newRoleForm" th:text="#{global.confirm}">Confirmar</button>
  423. </div>
  424. </div>
  425. </div>
  426. </div>
  427. <!-- contactModal -->
  428. <div th:replace="~{layouts/common.html :: contactModal}"></div>
  429. <!-- Footer -->
  430. <footer class="uv-footer uv-footer-text" th:replace="~{layouts/common.html :: footer}"></footer>
  431. <script th:src="@{/js/jquery/jquery.min.js}"></script>
  432. <script th:src="@{/js/fa/all.js}"></script>
  433. <script th:src="@{/js/jquery/datatables.min.js}"></script>
  434. <script th:src="@{/js/jquery-easing/jquery.easing.min.js}"></script>
  435. <script th:src="@{/js/bootstrap-select/bootstrap-select.js}"></script>
  436. <script th:src="@{/js/bootstrap-select/i18n/defaults-es_ES.js}" th:if="${#locale.language} == 'es'"></script>
  437. <script th:src="@{/js/bootstrap-select/i18n/defaults-ca_CA.js}" th:if="${#locale.language} != 'es'"></script>
  438. <script th:src="@{/js/gijgo/gijgo.min.js}"></script>
  439. <script th:src="@{/js/frappe-gantt/frappe-gantt.min.js}"></script>
  440. <script th:src="@{/js/chartjs/chart.js}"></script>
  441. <script th:src="@{/js/saic.js}"></script>
  442. <script th:if="${editable}" type="text/javascript">
  443. function editableSettings(){
  444. $(".custom-file-input").on("change", function() {
  445. var fileName = $(this).val().split("\\").pop();
  446. $(this).siblings(".custom-file-label").addClass("selected").html(fileName);
  447. });
  448. $("#newDocumentForm").submit(function(e) {
  449. e.preventDefault();
  450. var formData = new FormData(this);
  451. $.ajax({
  452. url: "/dashboard/documents",
  453. type: "POST",
  454. data: formData,
  455. processData: false,
  456. contentType: false,
  457. success: function(data){
  458. treeDocuments.reload();
  459. }
  460. });
  461. $('#categorySelector').val('').trigger('change');
  462. $('#documentSelector').val('').trigger('change');
  463. $('#customFile').val('').trigger('change');
  464. $('#newDocumentModal').modal('toggle');
  465. });
  466. }
  467. function hideAllDocuments(){
  468. $.post("/dashboard/documents/archive", {tlugar:tlugar, lugar:lugar}, function(){treeDocuments.reload();});
  469. $('#hideDocumentsModal').modal('toggle');
  470. }
  471. </script>
  472. <script th:if="${!editable}" type="text/javascript">
  473. function editableSettings(){}
  474. function hideAllDocuments(){}
  475. </script>
  476. <script type="text/javascript">
  477. var locale = '[[${#locale.language}]]';
  478. var lugar = '[[${organ.lugar}]]';
  479. var tlugar = '[[${organ.tlugar}]]';
  480. var ruct = '[[${organ.ruct}]]';
  481. var tambit = '[[${organ.tambit}]]';
  482. var treeProcedures, treeDocuments, ganttChart;
  483. var tasks = [];
  484. var inds = [];
  485. var cursorPosition;
  486. var tableAcreds, tableTits;
  487. $(document).ready(function(){
  488. layout = new Layout("");
  489. initTables();
  490. $('.data-inds-2').hide();
  491. $('.chart-container').hide();
  492. $('#categorySelector').val('').trigger('change');
  493. $('#categorySelector').change(getChildCats);
  494. $('.data-selector-orig').on('change', function(){
  495. //changeDataOrig($(this).val());
  496. });
  497. $('.data-selector-group').on('change', function(){
  498. //toggleDataView($(this).val());
  499. });
  500. $('.data-selector-orig').trigger('change');
  501. $('#newUserContainer').hide();
  502. $('#userSelector').val('').trigger('change');
  503. treeDocuments = $('#treeDocuments').tree({
  504. cascadeSelection: false,
  505. cascadeCheck: false,
  506. selectionType: 'single',
  507. dataSource: '/dashboard/documents/'+ruct
  508. });
  509. $('#newOrganForm').on('submit', function(e) {
  510. e.preventDefault();
  511. var type = $('#typeSelector').val();
  512. var endpoint = (type === 'centro') ? '/organ/centre' : '/organ/titulacion';
  513. var formData = new FormData(this);
  514. $.ajax({
  515. url: endpoint,
  516. type: 'PUT',
  517. data: formData,
  518. processData: false,
  519. contentType: false,
  520. success: function(data) {
  521. $('#newOrganModal').modal('hide');
  522. location.reload();
  523. }
  524. })
  525. })
  526. drawReports(ruct);
  527. drawGantt(ruct);
  528. editableSettings();
  529. layout.closeLoadingSpinner(".uv-loading-spinner");
  530. var hash = window.location.hash;
  531. if(hash){
  532. $('.nav-link').removeClass('active');
  533. $('.tab-pane').removeClass('active');
  534. $('.nav-link[href="'+hash+'"]').addClass('active');
  535. $(hash).addClass('active');
  536. }
  537. });
  538. function initTables(){
  539. $('#titsTable thead tr')
  540. .clone(true)
  541. .addClass('filters')
  542. .addClass('titsFilters')
  543. .appendTo('#titsTable thead');
  544. tableTits = $('#titsTable').DataTable({
  545. paging: true,
  546. buttons: [
  547. {
  548. extend: "pageLength",
  549. },
  550. {
  551. extend: 'excelHtml5',
  552. text: '<span><i class="fas fa-file-excel"></i></span>',
  553. titleAttr: 'Exportar Excel',
  554. title: 'ListadoTitulaciones',
  555. exportOptions: {
  556. columns: ':visible'
  557. }
  558. },
  559. {
  560. extend: 'csvHtml5',
  561. text: '<span><i class="fas fa-file-csv"></i></span>',
  562. titleAttr: 'Exportar CSV',
  563. title: 'ListadoTitulaciones',
  564. exportOptions: {
  565. columns: ':visible'
  566. }
  567. },
  568. {
  569. extend: 'pdfHtml5',
  570. text: '<span><i class="fas fa-file-pdf"></i></span>',
  571. titleAttr: 'Exportar PDF',
  572. title: 'ListadoTitulaciones',
  573. exportOptions: {
  574. columns: ':visible'
  575. }
  576. },
  577. {
  578. extend: 'colvis',
  579. text: '<span><i class="fas fa-eye"></i></span>',
  580. className: 'dropdown-colvis',
  581. titleAttr: 'Mostrar/Ocultar columnas'
  582. },
  583. {
  584. text: '<i class="fas fa-plus"></i>',
  585. titleAttr: 'Añadir nueva',
  586. action: function ( ) {
  587. $('#newOrganModal').modal('toggle');
  588. }
  589. }
  590. ],
  591. layout: {
  592. topStart: null,
  593. topEnd: 'buttons'
  594. },
  595. "language":{
  596. "lengthMenu": "[[#{admin.stats.table.lengthMenu}]]",
  597. "zeroRecords": "[[#{admin.stats.table.zeroRecords}]]",
  598. "info": "[[#{admin.stats.table.info}]]",
  599. "infoEmpty": "[[#{admin.stats.table.infoEmpty}]]",
  600. "infoFiltered": "[[#{admin.stats.table.infoFiltered}]]",
  601. "paginate": {
  602. "previous": "[[#{admin.stats.table.previous}]]",
  603. "next": "[[#{admin.stats.table.next}]]"
  604. },
  605. "buttons": {
  606. "pageLength": "%d [[#{admin.stats.table.lengthMenu}]]"
  607. }
  608. },
  609. bAutoWidth: false,
  610. aoColumns : [
  611. { sWidth: '5%' },
  612. { sWidth: '5%' },
  613. { sWidth: '35%' },
  614. { sWidth: '50%' },
  615. { sWidth: '5%' },
  616. { sWidth: '5%' },
  617. { sWidth: '5%' },
  618. { sWidth: '5%' },
  619. { sWidth: '5%' },
  620. { sWidth: '5%' },
  621. { sWidth: '5%' },
  622. { sWidth: '5%' }
  623. ],
  624. searching: true,
  625. order:[ [2, 'asc'], [4, 'asc'], [3, 'asc'] ],
  626. orderCellsTop: true,
  627. //fixedHeader: true, // Not compatible with android
  628. info: false,
  629. responsive: false,
  630. scrollX: true,
  631. lengthMenu: [10, 25, 50, 100, { label: 'All', value: -1 }],
  632. initComplete: function () {
  633. var api = this.api();
  634. this.api()
  635. .columns()
  636. .eq(0)
  637. .each(function (colIdx) {
  638. var cell = $('.filters.titsFilters th').eq(
  639. $(api.column(colIdx).header()).index()
  640. );
  641. var title = $(cell).text();
  642. $(cell).html('<input type="text" placeholder="' + title + '" />');
  643. $('input', $('.filters.titsFilters th').eq($(api.column(colIdx).header()).index()))
  644. .off('keyup change')
  645. .on('change', function (e) {
  646. $(this).attr('title', $(this).val());
  647. var regexr = '({search})';
  648. cursorPosition = this.selectionStart;
  649. api.column(colIdx)
  650. .search(
  651. this.value != ''
  652. ? regexr.replace('{search}', '(((' + this.value + ')))')
  653. : '',
  654. this.value != '',
  655. this.value == ''
  656. )
  657. .draw();
  658. })
  659. .on('keyup', function (e) {
  660. e.preventDefault();
  661. e.stopPropagation();
  662. $(this).trigger('change');
  663. $(this)
  664. .focus()[0]
  665. .setSelectionRange(cursorPosition, cursorPosition);
  666. });
  667. });
  668. api.column(0).visible(false);
  669. api.column(1).visible(false);
  670. api.column(5).visible(false);
  671. api.column(6).visible(false);
  672. api.column(7).visible(false);
  673. api.column(8).visible(false);
  674. api.column(9).visible(false);
  675. api.column(10).visible(false);
  676. api.column(11).visible(false);
  677. }
  678. });
  679. }
  680. function drawGantt(ructAttr){
  681. $.get('/dashboard/gantt/'+ructAttr, function(data){
  682. data.forEach(function(d){d.id = 'Task '+d.id;});
  683. data.at(-1).custom_class += ' gantt-task-last';
  684. tasks = data;
  685. ganttChart = new Gantt("#gantt", tasks, {
  686. on_click: function (task) {
  687. window.open("/procedure/"+task.id.replace('Task_', ''));
  688. },
  689. on_date_change: function(task, start, end) {
  690. return false;
  691. },
  692. on_progress_change: function(task, progress) {
  693. return false;
  694. },
  695. on_view_change: function() {
  696. var bars = document.querySelectorAll("#gantt" + " .bar-group");
  697. for (var i = 0; i < bars.length; i++) {
  698. bars[i].addEventListener("mousedown", stopEvent, true);
  699. }
  700. var handles = document.querySelectorAll("#gantt" + " .handle-group");
  701. for (var i = 0; i < handles.length; i++) {
  702. handles[i].remove();
  703. }
  704. },
  705. bar_height: 20,
  706. bar_corner_radius: 10,
  707. arrow_curve: 10,
  708. padding: 15,
  709. view_mode: "Month",
  710. date_format: "YYYY-MM-DD",
  711. language: locale,
  712. draggable: false,
  713. hasArrows: false,
  714. highlight_weekend: false,
  715. readonly: true,
  716. popup: function(task) {
  717. return `
  718. <div class="details-container">
  719. <h6>${task.name}</h6>
  720. <p>${task._start.toLocaleDateString()} - ${task._end.toLocaleDateString()}</p>
  721. </div>
  722. `;
  723. },
  724. });
  725. ganttChart.change_view_mode("Month");
  726. $('.today-button').trigger('click');
  727. $('.gantt-task-last').get(0).scrollIntoView({
  728. behavior: 'auto',
  729. block: 'center',
  730. inline: 'center'
  731. });
  732. $(window).scrollTop(0);
  733. });
  734. }
  735. function getGraphData(){
  736. $.get('/dashboard/graphs/inds/'+ruct+'/'+$('.data-selector-orig').val(), function(data){
  737. $('.chart-inds').html('');
  738. $('.data-inds').html('');
  739. createDataTables();
  740. inds.filter(d => d.estudi.toLowerCase() == $('.data-selector-orig').val().toLowerCase()).forEach(function(o){
  741. var datasets = [], labels = [];
  742. var vals = data.filter(d => d.dimension+'_'+d.indicador === o.idGrafica);
  743. if(vals.length < 1){ console.log(o.idGrafica); return; }
  744. createChartCanvas(o);
  745. vals.forEach(function(v){
  746. var values = [];
  747. v.valores.forEach(function(v, k){
  748. values.push(v.valor);
  749. labels.push(v.curso);
  750. });
  751. if(v.ambit === 'C'){
  752. datasets.push({
  753. label: locale == 'es' ? 'Centro' : 'Centre',
  754. data: values,
  755. borderWidth: 1.2,
  756. lineTension: 0.6,
  757. borderColor: "rgba(154,240,41,1)",
  758. backgroundColor: "rgba(154,240,41,0.5)",
  759. });
  760. drawTr(o, values.at(-1), values.at(-2));
  761. }
  762. if(v.ambit === 'U'){
  763. datasets.push({
  764. label: locale == 'es' ? 'Universidad' : 'Universitat',
  765. data: values,
  766. borderWidth: 1.2,
  767. lineTension: 0.6,
  768. borderColor: "rgba(255,0,0,1)",
  769. backgroundColor: "rgba(240,41,67,0.5)",
  770. });
  771. }
  772. });
  773. labels = Array.from(new Set(labels)).sort();
  774. drawChart(o, datasets, labels);
  775. $('.data-selector-group').val(2).trigger('change');
  776. });
  777. });
  778. }
  779. function createChartCanvas(i){
  780. $(`.chart-inds-${i.g}`).append(`<div class="col-lg-4 chart-container">
  781. <canvas id="chart_${i.idGrafica}"></canvas>
  782. </div>`);
  783. }
  784. function createDataTables(){
  785. $('.data-inds').html('');
  786. $('.data-inds').each(function(idx){
  787. var i = $(this).attr('data-id');
  788. var group = inds.filter(d => d.estudi.toLowerCase() == $('.data-selector-orig').val().toLowerCase()).filter(o => o.g == i);
  789. var trs = '';
  790. group.forEach(function(g){
  791. trs += `<tr id="tr_${g.idGrafica}"></tr>`;
  792. });
  793. $(this).html(`<table class="tableizer-table" style="width:100%;">
  794. <thead>
  795. <tr class="tableizer-firstrow">
  796. <th></th>
  797. <th style="width:50px;text-align: center;">Actual</th>
  798. <th style="width:50px;text-align: center;">Anterior</th>
  799. <th style="width:50px;text-align: center;">Desv.</th>
  800. </tr>
  801. </thead>
  802. <tbody>
  803. ${trs}
  804. </tbody>
  805. </table>`);
  806. });
  807. }
  808. function drawTr(obj, value1, value2){
  809. var color = (value2 == undefined) ? 'black' : value1 - value2 < 0 ? 'red' : 'green';
  810. if(obj.negate){
  811. color = (color == 'black') ? color : value1 - value2 > 0 ? 'red' : 'green';
  812. }
  813. $('#tr_'+obj.idGrafica).html(`<td>${locale == 'es' ? obj.nomCas : obj.nomVal}</td>
  814. <td>${parseFloat(value1).toFixed(2)}${obj.unitats}</td>
  815. <td>${value2 == undefined ? '---' : parseFloat(value2).toFixed(2)+obj.unitats}</td>
  816. <td style="color:${color}">${value2 == undefined ? '---' : (value1 - value2).toFixed(2)+obj.unitats}</td>`);
  817. }
  818. function drawChart(obj, values, labels){
  819. if(typeof Chart.getChart('chart_'+obj.idGrafica) !== 'undefined'){
  820. Chart.getChart('chart_'+obj.idGrafica).destroy();
  821. }
  822. const ctx = document.getElementById('chart_'+obj.idGrafica);
  823. var callback;
  824. if(obj.unitats == '%'){
  825. callback = function(value, index, values){return value + " %";};
  826. }
  827. else{
  828. callback = function(value, index, values){return value;};
  829. }
  830. new Chart(ctx, {
  831. type: 'line',
  832. data: {
  833. labels: labels,
  834. datasets: values
  835. },
  836. options: {
  837. responsive: true,
  838. maintainAspectRatio: false,
  839. plugins: {
  840. title: {
  841. display: true,
  842. text: locale == 'es' ? obj.nomCas : obj.nomVal
  843. },
  844. legend: {
  845. display: false
  846. },
  847. labels: {
  848. render: () => {}
  849. }
  850. },
  851. scales: {
  852. y: {
  853. max: obj.likert ? 5 : null,
  854. min: 0,
  855. beginAtZero: true,
  856. ticks: {
  857. beginAtZero: true,
  858. autoSkip: !obj.likert,
  859. stepSize: obj.likert ? 1 : null,
  860. stepValue: obj.likert ? 1 : null,
  861. callback: callback
  862. },
  863. grid: {
  864. drawBorder: true
  865. },
  866. gridLines: {
  867. display: true,
  868. color: "rgba(255,99,132,0.2)"
  869. },
  870. stacked: false,
  871. }
  872. }
  873. }
  874. });
  875. }
  876. function getChildCats(){
  877. $.get('/dashboard/documents/catsu/'+$(this).val()+'/'+tambit, function(data){
  878. $('#documentSelector').html('');
  879. data.forEach(function(d){
  880. $(`<option value="${d.idCategoria}" data-content="${locale == 'es' ? d.nomCas : d.nomVal}">${locale == 'es' ? d.nomCas : d.nomVal}</option>`)
  881. .appendTo($('#documentSelector'));
  882. });
  883. $('#documentSelector').selectpicker('refresh');
  884. });
  885. }
  886. function toggleDataView(id){
  887. $('.data-inds').hide();
  888. $('.data-inds-'+id).show();
  889. $('.chart-inds').hide();
  890. $('.chart-inds-'+id).show();
  891. }
  892. function changeDataOrig(id){
  893. $('option[data-group]').hide();
  894. $(`option[data-group*='${id}']`).show();
  895. getGraphData();
  896. }
  897. function stopEvent(event) {
  898. event.preventDefault();
  899. }
  900. function changeGanttOrigin(){
  901. ganttChart.clear();
  902. $('.gantt-container').parent().html('<svg id="gantt"></svg>')
  903. drawGantt($('#ganttOrigin').val());
  904. }
  905. function changeReportsOrigin(){
  906. treeProcedures.destroy();
  907. drawReports($('#reportsOrigin').val());
  908. }
  909. function drawReports(ructAttr){
  910. treeProcedures = $('#treeProcedures').tree({
  911. cascadeSelection: false,
  912. cascadeCheck: false,
  913. selectionType: 'single',
  914. dataSource: '/dashboard/procedures/'+ructAttr
  915. });
  916. }
  917. function toggleFormFields(value) {
  918. if (value === 'centro') {
  919. $('#sectionCentro').show();
  920. $('#sectionTitulacion').hide();
  921. $('#sectionTitulacion').find("input, select").prop("disabled", true);
  922. $('#sectionCentro').find("input, select").prop("disabled", false);
  923. } else {
  924. $('#sectionCentro').hide();
  925. $('#sectionTitulacion').show();
  926. $('#sectionCentro').find("input, select").prop("disabled", true);
  927. $('#sectionTitulacion').find("input, select").prop("disabled", false);
  928. }
  929. $('.selectpicker').selectpicker('refresh');
  930. }
  931. function newUser(){
  932. if(!$('#newUserContainer').is(":visible")){
  933. $('#firstname').val('').attr('required', true);
  934. $('#lastname').val('').attr('required', true);
  935. $('#username').val('').attr('required', true);
  936. $('#email').val('').attr('required', true);
  937. $('#newUserContainer').show();
  938. $('#userSelector').attr('required', false);
  939. $('#userSelectorContainer').hide();
  940. $('#addUserIcon').removeClass('fa-plus');
  941. $('#addUserIcon').addClass('fa-minus');
  942. }
  943. else{
  944. $('#firstname').val('').attr('required', false);
  945. $('#lastname').val('').attr('required', false);
  946. $('#username').val('').attr('required', false);
  947. $('#email').val('').attr('required', false);
  948. $('#newUserContainer').hide();
  949. $('#userSelector').attr('required', true);
  950. $('#userSelectorContainer').show();
  951. $('#addUserIcon').removeClass('fa-minus');
  952. $('#addUserIcon').addClass('fa-plus');
  953. }
  954. }
  955. function setEmail(){
  956. $('#email').val($('#username').val()+'@uv.es');
  957. }
  958. function addUserRole(event){
  959. event.preventDefault(); // Evita que la página se recargue
  960. const form = document.getElementById('newRoleForm');
  961. const formData = new FormData(form);
  962. $.ajax({
  963. type: "POST",
  964. url: "/admin/userrole/new",
  965. data: formData,
  966. processData: false, // Necesario para FormData
  967. contentType: false, // Necesario para FormData
  968. success: function(response) {
  969. if (response === true) {
  970. location.reload();
  971. } else {
  972. alert("Error al añadir el rol");
  973. }
  974. },
  975. error: function() {
  976. alert("No se pudo conectar con el servidor");
  977. }
  978. });
  979. }
  980. function deleteUserRole(event){
  981. event.preventDefault(); // Evita que la página se recargue
  982. const form = document.getElementById('deleteUserForm');
  983. const formData = new FormData(form);
  984. $.ajax({
  985. type: "POST",
  986. url: "/admin/userrole/remove",
  987. data: formData,
  988. processData: false, // Necesario para FormData
  989. contentType: false, // Necesario para FormData
  990. success: function(response) {
  991. if (response === true) {
  992. location.reload();
  993. } else {
  994. alert("Error al eliminar el rol");
  995. }
  996. },
  997. error: function() {
  998. alert("No se pudo conectar con el servidor");
  999. }
  1000. });
  1001. }
  1002. </script>
  1003. </body>
  1004. </html>