procedure.html 59 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334
  1. <!DOCTYPE html>
  2. <html xmlns="http://www.w3.org/1999/xhtml"
  3. xmlns:th="http://www.thymeleaf.org"
  4. lang="es">
  5. <head th:replace="~{layouts/common.html :: head}"></head>
  6. <style>
  7. svg[id^="mermaid-"] { min-width: 100%; font-size: 10px;}
  8. polygon.label-container {
  9. max-height: 50px !important;
  10. }
  11. g.node{
  12. max-height: 50px !important;
  13. }
  14. svg[aria-roledescription="flowchart-v2"]{
  15. -moz-transform: scale(0.75);
  16. -webkit-transform: scale(0.75);
  17. transform: scale(0.75);
  18. transform-origin: top center;
  19. top:0;
  20. margin-top:0px;
  21. margin-bottom:0px;
  22. }
  23. div.mermaid {
  24. margin-left: 0 !important;
  25. text-align: center;
  26. resize:both;
  27. overflow:auto;
  28. margin-bottom: 2px;
  29. position:relative;
  30. max-height: 600px;
  31. max-width: 100%;
  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="uv-table-group-procedure-info">
  45. <div class="uv-table-title">
  46. <span>
  47. <strong id="procedure_code" th:text="${instance.nomProces}"></strong>
  48. <strong th:text="${#locale.language} == 'es' ? ${instance.titolCas}:${instance.titolVal}"></strong>
  49. </span>
  50. <br>
  51. <span><strong th:text="#{procedures.center}"></strong>: <span th:text="${#locale.language} == 'es' ? ${instance.centreCas}:${instance.centreVal}"></span></span>
  52. <br>
  53. <span th:if="${instance.ruct != null}"><strong th:text="#{procedures.titulation}"></strong>: <a th:href="${'/dashboard/'+instance.ruct}"><span th:text="${#locale.language} == 'es' ? ${instance.titulacioCas}:${instance.titulacioVal}"></span></a></span>
  54. <span th:if="${instance.ruct == null}"><strong th:text="#{procedures.titulation}"></strong>: <span th:text="${#locale.language} == 'es' ? ${instance.titulacioCas}:${instance.titulacioVal}"></span></span>
  55. <br>
  56. <span><strong th:text="#{procedures.year.eval}"></strong>: <span><span th:text="${instance.cursAvaluat-1}"></span> - <span th:text="${instance.cursAvaluat}"></span></span></span>
  57. (<span th:text="#{procedures.year.act}"></span> <span th:text="${instance.cursActivacio-1}"></span> - <span th:text="${instance.cursActivacio}"></span>)
  58. <br>
  59. <p th:text="${#locale.language} == 'es' ? ${instance.descripcioCas}:${instance.descripcioVal}"></p>
  60. </div>
  61. <hr class="uv-procedure-hr">
  62. <div class="uv-table-group" th:if="${#authorization.expression('hasRole(''ROLE_ADMINS'')')}" style="text-align: right;">
  63. <div class="btn-toolbar" role="toolbar" style="margin-bottom:-30px;">
  64. <div class="btn-group mr-2 w-auto ml-auto" role="group" aria-label="First group">
  65. <button form="formInstanceClear" class="btn btn-secondary pointer" style="width:38px; height:38px;" th:title="#{admin.action.reloadInstance}"><i class="fas fa-redo"></i></button>
  66. <button form="formInstanceClose" class="btn btn-secondary pointer" style="width:38px; height:38px;" th:title="#{admin.action.closeInstance}"><i class="fas fa-lock"></i></button>
  67. <button form="formInstanceDelete" class="btn btn-danger pointer" style="width:38px; height:38px;" th:title="#{admin.action.deleteInstance}"><i class="fas fa-trash"></i></button>
  68. </div>
  69. </div>
  70. <form enctype='multipart/form-data' id="formInstanceClear" method="POST" action="/admin/instance/clear" style="display: inline;">
  71. <input type="hidden" name="idInstancia" th:value="${instance.idInstancia}">
  72. </form>
  73. <form enctype='multipart/form-data' id="formInstanceClose" method="POST" action="/admin/instance/close" style="display: inline;">
  74. <input type="hidden" name="idInstancia" th:value="${instance.idInstancia}">
  75. </form>
  76. <form enctype='multipart/form-data' id="formInstanceDelete" method="POST" action="/admin/instance/delete" style="display: inline;" th:if="${#authentication.principal.isAdmin()}">
  77. <input type="hidden" name="idInstancia" th:value="${instance.idInstancia}">
  78. </form>
  79. </div>
  80. </div>
  81. <div class="uv-table-group">
  82. <div class="col-sm-12 uv-table-section">
  83. <div class="uv-table-header">
  84. <h6 class="uv-table-header-h6"><span th:text="#{procedures.title.tasks}"></span>&nbsp;&nbsp;&nbsp;(<a href="javascript:void(0)" th:text="#{procedures.title.flow}" onclick="showFlowDiagram();">ver flujo</a>)</h6>
  85. <hr class="uv-table-header-hr">
  86. <div class="col">
  87. <span class="legend-active"><span th:text="#{procedures.legend.active}">Activa</span></span>
  88. <span class="legend-active-other"><span th:text="#{procedures.legend.other}">No corresponde</span></span>
  89. <span class="legend-expired"><span th:text="#{procedures.legend.expired}">Atrasada</span></span>
  90. <span class="legend-blocked"><span th:text="#{procedures.legend.blocked}">No procede</span></span>
  91. <span class="legend-done"><span th:text="#{procedures.legend.done}">Realizada</span></span>
  92. </div>
  93. </div>
  94. </div>
  95. <div id="flowDiagramContainer" class="flowDiagramContainer" style="display: none;">
  96. <button type="button" class="close" onclick="hideFlowDiagram();" style="margin-top:-50px;">
  97. <span aria-hidden="true" style="font-size:125%;"><i class="fas fa-window-close"></i></span>
  98. </button>
  99. <div class="btn-toolbar mermaid-tools" role="toolbar" style="float:left;">
  100. <div class="btn-group w-auto ml-auto pointer" role="group" aria-label="downloadDiagram">
  101. <span class="btn btn-secondary pointer" style="width:38px; height:38px; padding:6px" onclick="downloadFlowDiagram();"><i class="fas fa-download"></i></span>
  102. </div>
  103. </div>
  104. <div class="mermaid" th:text="${flow}"></div>
  105. </div>
  106. </div>
  107. <div class="uv-accordion" style="margin-top:20px;text-align:center;">
  108. <div id="accordion">
  109. <div class="card" th:each="item,i : ${tasks}">
  110. <input type="hidden" id="activeOptions" th:if="${item.estat == 'A'}" th:value="${item.opcions}">
  111. <div class="card-header pointer"
  112. th:classappend="${(item.estat == 'A' and item.isAssignedToUser?'card-header-active ':'') + (item.estat == 'A' and not item.isAssignedToUser?'card-header-active-other ':'') + (item.isExpired and item.estatInstancia == 'A' and item.estat == 'A'?'card-header-expired ':'') + (item.estat == null?'card-header-blocked ':'')}"
  113. th:attr="id='heading'+${i.index},data-target='#collapse'+${i.index},aria-controls='#collapse'+${i.index}, data-toggle=${item.estat != null or #authorization.expression('hasRole(''ROLE_W'')') ? 'collapse' : ''}"
  114. aria-expanded="true" onclick="showComments();">
  115. <span style="float:left;font-size: 21px; margin-left:-10px;margin-top:-10px;" th:if="${item.estat != null or #authorization.expression('hasRole(''ROLE_W'')')}"><i class="fas fa-caret-down"></i></span>
  116. <h6 style="text-align:left; margin-left:15px;">
  117. <strong th:text="${i.index+1}+'/'+${#lists.size(tasks)}"></strong><strong> - </strong>
  118. <span th:text="${#locale.language} == 'es' ? ${item.titolCas}:${item.titolVal}"></span>
  119. <br>
  120. <span th:text="#{procedures.status}"></span>:
  121. <span th:if="${item.estat == 'A'}" th:text="#{procedures.status.a}"></span>
  122. <span th:if="${item.estat == 'S'}" th:text="#{procedures.status.s}"></span>
  123. <span th:if="${item.estat == 'N'}" th:text="#{procedures.status.n}"></span>
  124. <span th:if="${item.estat == 'E'}" th:text="#{procedures.status.e}"></span>
  125. <span th:if="${item.estat == 'F'}" th:text="#{procedures.status.f}"></span>
  126. <span th:if="${item.estat == null}" th:text="#{procedures.status.i}"></span>
  127. <span>
  128. <br>
  129. <span th:text="#{procedures.assignedTo}"></span>:
  130. <span th:text="${#locale.language} == 'es' ? ${item.descripcioRolCas}:${item.descripcioRolVal}"></span>
  131. </span>
  132. <br>
  133. <span th:if="${item.estat == 'A' or item.estat == null}"><span th:text="#{procedures.dateLimit}"></span>: <span th:text="${item.dataLim}"></span></span>
  134. <span style="float: right; margin-top:-5px;" th:if="${item.estat == 'A' and item.isAssignedToUser}" th:attr="title=#{procedures.status.title.a}"><i class="fa fa-play-circle"></i></span>
  135. <span style="float: right; margin-top:-5px;" th:if="${item.estat == 'A' and not item.isAssignedToUser}" th:attr="title=#{procedures.status.title.o}"><i class="fa fa-exclamation-triangle"></i></span>
  136. <span style="float: right; margin-top:-5px;" th:if="${item.estat == null}"><i class="fa fa-lock" th:attr="title=#{procedures.status.title.p}"></i></span>
  137. <span style="float: right; margin-top:-5px;" th:if="${item.estat != null and item.estat != 'A'}" th:attr="title=#{procedures.status.title.r}"><i class="fa fa-check"></i></span>
  138. <span style="float: right; margin-top:-5px; margin-right:10px;" th:if="${(item.estat != 'A' and item.estat != null and #lists.contains({1, 11, 12, 13, 14, 15, 21, 22}, item.tipus)) or (item.estat == 'A' and item.evidencia != null)}">
  139. <a th:href="${'/download/'+item.idInstanciaTasca}" target="_blank"><i class="fas fa-file-pdf" style="font-size:200%;margin-top:-10px;"></i></a>
  140. </span>
  141. </h6>
  142. </div>
  143. <div th:attr="id='collapse'+${i.index},aria-labelledby='heading'+${i.index}" class="collapse" data-parent="#accordion">
  144. <div class="card-body uv-card-body">
  145. <div id="taskAdminActions" th:if="${#authorization.expression('hasRole(''ROLE_W'')')}" style="margin-bottom: 20px;">
  146. <div class="btn-toolbar" role="toolbar" style="margin-top:-15px; margin-bottom:-15px;">
  147. <div class="btn-group mr-2 w-auto ml-auto" role="group" aria-label="First group">
  148. <button type="submit" th:attr="form=${'formTaskReload'+item.idInstanciaTasca}" class="btn btn-secondary pointer" style="width:38px; height:38px;" th:title="#{admin.action.reloadTask}"><i class="fas fa-redo"></i></button>
  149. <button type="submit" th:attr="form=${'formTaskReactivate'+item.idInstanciaTasca}" class="btn btn-secondary pointer" style="width:38px; height:38px;" th:title="#{admin.action.reactivate}" th:if="${item.estat != 'A' and item.estat != null}"><i class="fas fa-lock-open"></i></button>
  150. <button type="submit" th:attr="form=${'formTaskEdit'+item.idInstanciaTasca}" class="btn btn-secondary pointer" style="width:38px; height:38px;" th:title="#{admin.action.edit}" th:if="${item.estat != 'A' and item.estat != null and #lists.contains({11, 15}, item.tipus)}"><i class="fas fa-edit"></i></button>
  151. <button type="submit" th:attr="form=${'formShowDrafts'+item.idInstanciaTasca}" class="btn btn-secondary pointer" style="width:38px; height:38px;" th:title="'Ver borradores'" th:if="${item.estat != null and #authentication.principal.isAdmin() and #lists.contains({11, 15}, item.tipus)}"><i class="fas fa-code-branch"></i></button>
  152. <button th:attr="id=${'filepicker-creator'+item.idInstanciaTasca}" class="btn btn-secondary pointer" style="width:38px; height:38px;" th:title=#{admin.action.attach} th:onclick="'createFilepicker(\''+${instance.idInstancia}+'\', \''+${item.idInstanciaTasca}+'\')'" th:if="${item.estat != 'A' and item.estat != null and #lists.contains({1, 11, 12, 13, 14, 15}, item.tipus)}"><i class="fas fa-file-upload"></i></button>
  153. </div>
  154. </div>
  155. <form th:id="${'formTaskReload'+item.idInstanciaTasca}" enctype='multipart/form-data' method="POST" action="/admin/instance/task/reload" style="display: inline;">
  156. <input type="hidden" name="idInstanciaTasca" th:value="${item.idInstanciaTasca}">
  157. </form>
  158. <form th:id="${'formTaskReactivate'+item.idInstanciaTasca}" enctype='multipart/form-data' method="POST" action="/admin/instance/task/reactivate" style="display: inline;" th:if="${item.estat != 'A'}">
  159. <input type="hidden" name="idInstanciaTasca" th:value="${item.idInstanciaTasca}">
  160. </form>
  161. <form th:id="${'formTaskEdit'+item.idInstanciaTasca}" enctype='multipart/form-data' method="POST" action="/admin/instance/task/edit" style="display: inline;" th:if="${item.estat != 'A' and #lists.contains({11, 15}, item.tipus)}">
  162. <input type="hidden" name="idInstanciaTasca" th:value="${item.idInstanciaTasca}">
  163. </form>
  164. <form th:id="${'formShowDrafts'+item.idInstanciaTasca}" enctype='multipart/form-data' method="GET" onsubmit="target_popup(this);" th:action="${'/procedure/drafts/'+item.idInstanciaTasca}" target="_blank" style="display: inline;" th:if="${#authentication.principal.isAdmin() and #lists.contains({11, 15}, item.tipus)}">
  165. </form>
  166. </div>
  167. <div class="row" style="margin-bottom:20px;text-align: left;width: 90%;">
  168. <span th:text="${#locale.language} == 'es' ? ${item.descripcioCas}:${item.descripcioVal}"></span>
  169. </div>
  170. <div class="row" th:if="${item.estat == 'A'}">
  171. <script>
  172. var typeActive = "[[${item.tipus}]]";
  173. var idInstanciaTasca = "[[${item.idInstanciaTasca}]]";
  174. </script>
  175. <div class="card-body-inner-action" th:if="${item.isAssignedToUser or #authorization.expression('hasRole(''ROLE_W'')')}">
  176. <form enctype='multipart/form-data' method="POST" th:action="${'/procedure/'+instance.idInstancia}">
  177. <input class="active-task" type="hidden" name="taskid" th:value="${item.idInstanciaTasca}">
  178. <input class="active-task" type="hidden" name="procid" th:value="${instance.idInstancia}">
  179. <div class="card-body-inner-action" th:if="${item.tipus == 4}"> </div>
  180. <div class="card-body-inner-action" th:if="${item.tipus == 2}">
  181. <span id="action_y" class="btn btn-y pointer" th:text="#{procedures.btn.yes}" onclick="click_y();"></span> <span style="margin-left:10px;margin-right:10px;"></span>
  182. <span id="action_n" class="btn btn-n pointer" th:text="#{procedures.btn.no}" onclick="click_n();"></span>
  183. <input id="response" name="response" value="" required style="opacity: 0;">
  184. </div>
  185. <div class="card-body-inner-action" th:if="${item.tipus == 11 or item.tipus == 15}">
  186. <textarea id="editor" name="evidencia_text" th:text="${item.text}"></textarea>
  187. <div style="text-align: left;">
  188. <small style="text-align: left;font-weight: bold;"><span th:text="#{procedures.autosave.lastdate}">Última modificación</span>: <span th:if="${item.dataMod != null}" id="lastmoddate" th:text="${item.dataMod}"></span><span th:if="${item.dataMod == null}" id="lastmoddate" th:text="#{procedures.autosave.none}"></span></small>
  189. </div>
  190. </div>
  191. <div class="card-body-inner-action" th:if="${#lists.contains({1, 12, 13, 14}, item.tipus)}">
  192. <div class="row">
  193. <div class="col" style="text-align: left;margin-bottom:20px;" th:if="${!#strings.equals(item.codiEvidencia, '0')}">
  194. <span th:text="#{procedures.template}"></span>:
  195. <a th:href="${'/download/template/'+item.idInstanciaTasca}" target="_blank">
  196. <strong th:text="${item.codiEvidencia}"></strong> <span th:text="${#locale.language} == 'es' ? ${item.nomEvidenciaCas}:${item.nomEvidenciaVal}"></span>
  197. <i class="fas fa-file-pdf"></i>
  198. </a>
  199. </div>
  200. </div>
  201. <div class="file-loading">
  202. <input id="filepicker" multiple type="file" name="evidencias">
  203. </div>
  204. <br>
  205. <div class="row card-body-up-btn" th:if="${item.oldEvidences != null}">
  206. <a data-toggle="collapse" th:attr="href='#collapseEvidences'+${item.idInstanciaTasca}, aria-controls='#collapseEvidences'+${item.idInstanciaTasca}"
  207. role="button" aria-expanded="false" th:text="#{procedures.olderEvidences}">
  208. Evidencias generadas en el proceso anteriormente
  209. </a>
  210. <div class="collapse" th:attr="id='collapseEvidences'+${item.idInstanciaTasca}" style="width:95%;">
  211. <div class="card card-body" th:each="ev : ${item.oldEvidences}">
  212. <a th:href="${'/download/'+ev.idTascai}" target="_blank"><span></span> <span th:text="${ev.codiEvidencia}"></span> <span th:text="${#locale.language} == 'es' ? ${ev.nomEvidenciaCas}:${ev.nomEvidenciaVal}"></span> [<span th:text="${#locale.language} == 'es' ? 'Curso evaluado ':'Curs avaluat '"></span><span th:text="${ev.curs-1}"></span> - <span th:text="${ev.curs}"></span>]</a>
  213. </div>
  214. </div>
  215. </div>
  216. </div>
  217. <div class="card-body-inner-action" th:if="${item.tipus == 3}">
  218. <div id="pamSplitContainer" class="split-view-conttainer">
  219. </div>
  220. <div class="table-responsive">
  221. <table class="table table-bordered uv-table-pam-modern" id="pamTable">
  222. <thead>
  223. <tr>
  224. <th colspan="3">ORIGEN DE LA ACCIÓN</th>
  225. <th colspan="5">ACCIÓN PLANIFICADA</th>
  226. <th colspan="3">SEGUIMIENTO</th>
  227. </tr>
  228. <tr class="subheader">
  229. <th class="col-id">ID</th>
  230. <th>Objetivo de mejora</th>
  231. <th>Origen</th>
  232. <th>Actividades a desarrollar</th>
  233. <th>Responsable</th>
  234. <th class="col-prioridad">Prioridad</th>
  235. <th class="col-curso">Curso Inicio</th>
  236. <th class="col-curso">Curso Fin</th>
  237. <th>Indicadores de seguimiento</th>
  238. <th class="col-seguimiento">Grado de ejecución de las acciones</th>
  239. <th>Justificación</th>
  240. </tr>
  241. </thead>
  242. <tbody>
  243. </tbody>
  244. </table>
  245. </div>
  246. <div class="text-left mt-3 mb-4">
  247. <button type="button" class="btn btn-outline-primary btn-sm" onclick="addNewRowPAM()">
  248. <i class="fas fa-plus"></i> Añadir otra fila al Plan de Acción
  249. </button>
  250. </div>
  251. <div id="pdfWidget" class="pdf-widget">
  252. <div id="pdfWidgetHeader" class="pdf-widget-header">
  253. <i class="fas fa-file-pdf"></i>
  254. <span>Informe</span>
  255. </div>
  256. </div>
  257. <div id="pdfPreviewContainer" class="pdf-preview-side">
  258. <div class="pdf-preview-header">
  259. <span><i class="fas fa-info-circle mr-2"></i> Documentación de apoyo</span>
  260. <button type="button" class="btn-close-pdf" onclick="closePdfPreview(event)">
  261. &times;
  262. </button>
  263. </div>
  264. <div style="flex-grow: 1; background: #525659;">
  265. <iframe th:src="@{/E_PG1_1.pdf}" frameborder="0" style="width:100%; height:100%;"></iframe>
  266. </div>
  267. </div>
  268. </div>
  269. <div class="row card-body-up-btn">
  270. <span class="btn btn-primary pointer" th:if="${#lists.contains({11, 15}, item.tipus)}" style="margin-right:10px;" onclick="saveEditorWithConfirmation();"><i class="fas fa-save"></i> <span th:text="#{procedures.btn.draft}">Guardar borrador</span></span>
  271. <span class="btn btn-primary pointer" th:if="${item.tipus == 3}"
  272. style="margin-right:10px;" onclick="processPAMTable('draft');">
  273. <i class="fas fa-save"></i> <span th:text="#{procedures.btn.draft}">Guardar borrador</span>
  274. </span>
  275. <button class="btn btn-success" th:if="${not #lists.contains({1, 3, 11, 12, 13, 14, 15}, item.tipus)}">Confirmar</button>
  276. <button class="btn btn-success" th:if="${#lists.contains({11, 15}, item.tipus)}" data-toggle="confirmation" th:attr="data-title=#{procedures.editor.confirm}"><i class="fas fa-file-import"></i> Enviar</button>
  277. <span class="btn btn-success pointer" th:if="${#lists.contains({1, 12, 13, 14}, item.tipus)}" onclick="$('#filepicker').fileinput('upload');">Confirmar</span>
  278. <button type="button" class="btn btn-success" th:if="${item.tipus == 3}"
  279. onclick="processPAMTable('submit');">
  280. <i class="fas fa-check-circle"></i> Confirmar
  281. </button>
  282. </div>
  283. </form>
  284. </div>
  285. </div>
  286. <div class="row" th:if="${item.estat != 'A' and item.estat != null and (#lists.contains({1, 12, 13, 14, 22}, item.tipus)) and (#authentication.principal.isGranted() or #authentication.principal.isAdmin())}">
  287. <!-- Añadir más evidencias (solo uq y admin) -->
  288. <form class="col" enctype='multipart/form-data' method="POST" th:action="${'/procedure/'+instance.idInstancia}">
  289. <input type="hidden" name="taskid" th:value="${item.idInstanciaTasca}">
  290. <input type="hidden" name="procid" th:value="${instance.idInstancia}">
  291. <div class="additional-file-container" th:attr="id='additionalFileContainer'+${item.idInstanciaTasca}"></div>
  292. </form>
  293. </div>
  294. <div class="row" th:unless="${item.estat == 'A'}">
  295. <form enctype='multipart/form-data' method="POST" action="/admin/instance/task/clear" style="display:inline; margin-right:20px;" th:if="${item.estat != null and (#authentication.principal.isAdmin())}">
  296. <input type="hidden" name="idInstanciaTasca" th:value="${item.idInstanciaTasca}">
  297. <div class="btn-toolbar" role="toolbar" style="margin-top:20px;">
  298. <div class="btn-group w-auto ml-auto" role="group" aria-label="First group" style="margin-left:0px;">
  299. <button class="btn btn-danger pointer" style="width:30px; height:30px; padding:0;" th:title="#{admin.action.deleteTask}"><i class="fas fa-trash"></i></button>
  300. </div>
  301. </div>
  302. </form>
  303. <span style="text-align:left;" th:if="${item.estat != null}">
  304. <span th:if="${item.tipus == 4}"> <!-- avanzar -->
  305. <span th:text="#{procedures.done.response}"></span>: <span th:text="#{procedures.status.f}"></span>
  306. </span>
  307. <span th:if="${item.tipus == 2 and item.estat == 'S'}"> <!-- pregunta resp si -->
  308. <span th:text="#{procedures.done.response}"></span>: <span th:text="#{procedures.done.response.s}"></span>
  309. </span>
  310. <span th:if="${item.tipus == 2 and item.estat == 'N'}"> <!-- pregunta resp no -->
  311. <span th:text="#{procedures.done.response}"></span>: <span th:text="#{procedures.done.response.n}"></span>
  312. </span>
  313. <span th:if="${#lists.contains({1, 11, 12, 13, 14, 15, 21, 22}, item.tipus)}"> <!-- evidencia -->
  314. <span th:text="#{procedures.done.evidence}"></span>:
  315. <a th:href="${'/download/'+item.idInstanciaTasca}" target="_blank">
  316. <strong th:text="${item.codiEvidencia}"></strong> <span th:text="${#locale.language} == 'es' ? ${item.nomEvidenciaCas}:${item.nomEvidenciaVal}"></span>
  317. <i class="fas fa-file-pdf"></i>
  318. </a>
  319. </span>
  320. <br>
  321. <span><span th:text="#{procedures.done.user}"></span>: <span th:text="${item.usuariFet}"></span></span>
  322. <br>
  323. <span><span th:text="#{procedures.done.date}"></span>: <span th:text="${item.dataFet}"></span></span>
  324. </span>
  325. </div>
  326. <div class="container" th:if="${item.versions != null}" style="margin-top:25px;text-align:left;margin-left: 0px;">
  327. <strong th:if="${#lists.size(item.versions) > 0}" th:text="#{procedures.otherActions}">Acciones realizadas anteriormente</strong>
  328. <br>
  329. <div class="row" style="text-align:left;" th:if="${#lists.size(item.versions) > 0}" th:each="version,i : ${item.versions}">
  330. <hr class="uv-procedure-hr" style="width:100%;text-align:left;margin-left: 0px;">
  331. <div class="col-1" th:if="${#authentication.principal.isAdmin() or #authentication.principal.isGranted()}" style="flex:0 0 0;width:50px !important;padding:0;">
  332. <form enctype='multipart/form-data' method="POST" action="/admin/instance/task/remove" style="display: inline;">
  333. <input type="hidden" name="idInstanciaTasca" th:value="${version.idInstanciaTasca}">
  334. <div class="btn-toolbar" role="toolbar" style="margin-top:20px;">
  335. <div class="btn-group w-auto ml-auto" role="group" aria-label="First group" style="margin-left:0px;">
  336. <button class="btn btn-danger pointer" style="width:30px; height:30px; padding:0;" th:title="#{admin.action.deleteTask}"><i class="fas fa-trash"></i></button>
  337. </div>
  338. </div>
  339. </form>
  340. </div>
  341. <div class="col-11">
  342. <span th:if="${version.tipus == 4}"> <!-- avanzar -->
  343. <span th:text="#{procedures.done.response}"></span>: <span th:text="#{procedures.status.f}"></span>
  344. </span>
  345. <span th:if="${version.tipus == 2 and version.estat == 'S'}"> <!-- pregunta resp si -->
  346. <span th:text="#{procedures.done.response}"></span>: <span th:text="#{procedures.done.response.s}"></span>
  347. </span>
  348. <span th:if="${version.tipus == 2 and version.estat == 'N'}"> <!-- pregunta resp no -->
  349. <span th:text="#{procedures.done.response}"></span>: <span th:text="#{procedures.done.response.n}"></span>
  350. </span>
  351. <span th:if="${version.tipus == 1 or version.tipus == 11 or version.tipus == 15}"> <!-- evidencia -->
  352. <span th:text="#{procedures.done.evidence}"></span>:
  353. <a th:href="${'/download/'+version.idInstanciaTasca}" target="_blank">
  354. <strong th:text="${version.codiEvidencia}"></strong> <span th:text="${#locale.language} == 'es' ? ${version.nomEvidenciaCas}:${version.nomEvidenciaVal}"></span>
  355. <i class="fas fa-file-pdf"></i>
  356. </a>
  357. </span>
  358. <br>
  359. <span><span th:text="#{procedures.done.user}"></span>: <span th:text="${version.usuariFet}"></span></span>
  360. <br>
  361. <span><span th:text="#{procedures.done.date}"></span>: <span th:text="${version.dataFet}"></span></span>
  362. </div>
  363. <br>
  364. </div>
  365. </div>
  366. </div>
  367. </div>
  368. </div>
  369. </div>
  370. </div>
  371. <div class="uv-table-group procedure-status" th:if="${instance.estat == 'F'}">
  372. <strong th:text="#{procedures.closed}"></strong>
  373. </div>
  374. <div class="uv-table-group procedure-status" th:if="${instance.estat == 'C'}">
  375. <strong th:text="#{procedures.cancelled}"></strong>
  376. </div>
  377. <div class="uv-table-group procedure-status" th:if="${location == 'supervision'}">
  378. <a href="/supervision/search" class="btn btn-primary pointer" th:text="#{procedures.back}">Volver</a>
  379. </div>
  380. </div>
  381. </div>
  382. </div>
  383. <div class="modal fade" id="flowDiagramModal" tabindex="-1" role="dialog" aria-labelledby="flowDiagramModal" aria-hidden="true">
  384. <div class="modal-dialog modal-xl" >
  385. <div class="modal-content">
  386. <div class="modal-header">
  387. <h5 class="modal-title">Flujo del procedimiento</h5>
  388. <button type="button" class="close" data-dismiss="modal" aria-label="Close">
  389. <span aria-hidden="true">&times;</span>
  390. </button>
  391. </div>
  392. <div class="modal-body">
  393. <div class="mermaid-def" th:text="${flow}" style="display:none;"></div>
  394. <div id="flowDiagram" class="mermaid"></div>
  395. </div>
  396. <div class="modal-footer">
  397. <button type="button" class="btn btn-secondary" data-dismiss="modal" th:text="#{global.cancel}">Cerrar</button>
  398. </div>
  399. </div>
  400. </div>
  401. </div>
  402. <!-- contactModal -->
  403. <div th:replace="~{layouts/common.html :: contactModal}"></div>
  404. <!-- Footer -->
  405. <footer class="uv-footer uv-footer-text" th:replace="~{layouts/common.html :: footer}"></footer>
  406. <script th:src="@{/js/jquery/jquery.min.js}"></script>
  407. <script th:src="@{/js/bootstrap/bootstrap.min.js}"></script>
  408. <script th:src="@{/js/fa/all.js}"></script>
  409. <script th:src="@{/js/jquery/jquery.dataTables.min.js}"></script>
  410. <script th:src="@{/js/jquery/jquery.dataTables.responsive.min.js}"></script>
  411. <script th:src="@{/js/jquery/jquery.dataTables.rowReorder.min.js}"></script>
  412. <script th:src="@{/js/bootstrap/dataTables.bootstrap4.min.js}"></script>
  413. <script th:src="@{/js/jquery-easing/jquery.easing.min.js}"></script>
  414. <script th:src="@{/js/saic.js}"></script>
  415. <script th:src="@{/js/datepicker/bootstrap-datepicker.min.js}"></script>
  416. <script th:src="@{/js/fileinput/js/fileinput.min.js}"></script>
  417. <script th:src="@{/js/fileinput/themes/fas/theme.js}"></script>
  418. <script th:src="@{/js/fileinput/js/locales/es.js}" th:if="${#locale.language == 'es'}"></script>
  419. <script th:src="@{/js/fileinput/js/locales/ca.js}" th:if="${#locale.language != 'es'}"></script>
  420. <script th:src="@{/js/confirmation/bootstrap-confirmation.js}"></script>
  421. <script src="https://cdn.jsdelivr.net/npm/mermaid@11.3.0/dist/mermaid.min.js"></script>
  422. <script src="https://unpkg.com/@popperjs/core@2"></script>
  423. <script src="https://unpkg.com/tippy.js@6"></script>
  424. <link rel="stylesheet" href="https://unpkg.com/tippy.js@6/dist/tippy.css" />
  425. <link rel="stylesheet" href="https://unpkg.com/tippy.js@6/themes/light.css" />
  426. <script th:inline="javascript" th:if="${#authentication.principal.isAdmin()}">
  427. var tinyobjectresizing = true;
  428. </script>
  429. <script th:inline="javascript" th:if="${#authentication.principal.isAdmin() or #authentication.principal.isGranted()}">
  430. if (!window.Worker) {
  431. alert(/*[[#{procedures.nosupported}]]*/);
  432. window.location = '/';
  433. }
  434. var aliveWorker;
  435. var saveWorker;
  436. function confirmDelete(){ }
  437. function confirmClear(){ }
  438. function setSaveInterval(){ console.log('Autosave disabled'); }
  439. function setAliveInterval(){
  440. console.log('KeepAlive enabled');
  441. aliveWorker = new Worker(/*[[@{/js/workers/alive_worker.js}]]*/);
  442. aliveWorker.onmessage = keepalive;
  443. }
  444. var tinyplugins = 'preview importcss searchreplace autolink autosave save directionality code visualblocks visualchars fullscreen image link media codesample table charmap pagebreak nonbreaking anchor insertdatetime advlist lists charmap quickbars checklist tinycomments codeeditor paste';
  445. var tinytoolbar = 'save | undo redo | bold italic underline strikethrough forecolor backcolor removeformat | fontfamily fontsize | alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist checklist | showcomments | btn-editable btn-noneditable insert-data | codeeditor | topdf fullscreen ';
  446. var tinymenu = 'file edit view insert format tools table help';
  447. var tinytable = 'table tabledelete | tableprops tablerowprops tablecellprops | tableinsertrowbefore tableinsertrowafter tabledeleterow | tableinsertcolbefore tableinsertcolafter tabledeletecol';
  448. var tinycontextmenu = 'link table useBrowserSpellcheck';
  449. var tinytableappearance = true;
  450. var tinytableclass = [
  451. {title: 'Normal', value: ''},
  452. {title: 'Editable', value: 'mceEditable'},
  453. {title: 'No editable', value: 'mceNonEditable'},
  454. {title: 'Ignorar en el PDF', value: 'mceNonEditable pdfignore'}
  455. ];
  456. var tinytableresizebars = true;
  457. var tinyadvtab = true;
  458. var tinyobjectresizing = false;
  459. var tinypasteplain = false;
  460. var tinypasteremovestyles = true;
  461. const widget = document.getElementById("pdfWidget");
  462. const preview = document.getElementById("pdfPreviewContainer");
  463. function tinyconfigure(editor){
  464. editor.on('keydown', function(e) {
  465. if (e.keyCode == 8 || e.keyCode == 46) { //backspace and delete keycodes
  466. try {
  467. var elem = tinymce.activeEditor.selection.getNode(); //current caret node
  468. if (elem.classList.contains("mceNonEditable")) {
  469. e.preventDefault();
  470. return false;
  471. }
  472. } catch (e) {console.log(e);}
  473. }
  474. });
  475. editor.on('TableModified', function(e) {
  476. editor.setContent(editor.getContent());
  477. });
  478. editor.ui.registry.addIcon('save', '<i style="font-size:125%;" class="fas fa-save"></i>')
  479. editor.ui.registry.addButton('topdf', {
  480. icon: 'print',
  481. tooltip: 'Imprimir a PDF',
  482. onAction: function(_){
  483. print_to_pdf(editor.getContent());
  484. }
  485. });
  486. editor.on('FullscreenStateChanged', function () {
  487. if(tinymce.activeEditor.plugins.fullscreen.isFullscreen()){
  488. var e = '<span id="exitFullScreen" class="btn btn-primary pointer" style="position:absolute;display:none;float:right;right:15px;top:25px;color:white;background-color:#007bff;padding:8px;cursor:pointer;" onclick="tinymce.activeEditor.execCommand('+"'mceFullScreen'"+');">Volver a SAIC <i class="fas fa-compress-arrows-alt"></i></span>';
  489. $(e).appendTo($($('.tox-editor-container').get(0)));
  490. $('#exitFullScreen').show();
  491. }
  492. else{
  493. $('#exitFullScreen').remove();
  494. }
  495. });
  496. editor.ui.registry.addButton('btn-editable', {
  497. icon: 'unlock',
  498. tooltip: 'Desbloquear contenido',
  499. onAction: function(_){
  500. var ns = $(editor.selection.getSelectedBlocks()).each(function(b){
  501. $(this).removeClass('mceEditable').removeClass('mceNonEditable').addClass('mceEditable')
  502. });
  503. editor.setContent(editor.getContent());
  504. }
  505. });
  506. editor.ui.registry.addButton('btn-noneditable', {
  507. icon: 'lock',
  508. tooltip: 'Bloquear contenido',
  509. onAction: function(_){
  510. var ns = $(editor.selection.getSelectedBlocks()).each(function(b){
  511. $(this).removeClass('mceEditable').removeClass('mceNonEditable').addClass('mceNonEditable')
  512. });
  513. editor.setContent(editor.getContent());
  514. }
  515. });
  516. editor.ui.registry.addMenuItem("useBrowserSpellcheck", {
  517. text: "Use `Ctrl + Click derecho` para acceder al corrector",
  518. onAction: function () {
  519. editor.notificationManager.open({
  520. text:"Para usar el corrector, mantenga pulsada la tecla Control (Ctrl) y haga click derecho sobre la palabra.",
  521. type: "info",
  522. timeout: 5000,
  523. closeButton: true
  524. });
  525. }
  526. });
  527. editor.ui.registry.addContextMenu("useBrowserSpellcheck", {
  528. update: function (node) {
  529. return editor.selection.isCollapsed() ? ["useBrowserSpellcheck"] : [];
  530. }
  531. });
  532. }
  533. </script>
  534. <script th:inline="javascript" th:unless="${#authentication.principal.isAdmin() or #authentication.principal.isGranted()}">
  535. if (!window.Worker) {
  536. alert(/*[[#{procedures.nosupported}]]*/);
  537. window.location = '/';
  538. }
  539. var aliveWorker;
  540. var saveWorker;
  541. function confirmDelete(){ }
  542. function confirmClear(){ }
  543. function setSaveInterval(){
  544. console.log('AutoSave enabled');
  545. saveWorker = new Worker(/*[[@{/js/workers/autosave_worker.js}]]*/);
  546. saveWorker.onmessage = triggerSave;
  547. }
  548. function setAliveInterval(){
  549. console.log('KeepAlive enabled');
  550. aliveWorker = new Worker(/*[[@{/js/workers/alive_worker.js}]]*/);
  551. aliveWorker.onmessage = keepalive;
  552. }
  553. var tinyplugins = 'preview importcss searchreplace autolink autosave save directionality code visualblocks visualchars fullscreen image link media codesample table charmap pagebreak nonbreaking anchor insertdatetime advlist lists charmap quickbars checklist tinycomments paste';
  554. var tinytoolbar = 'save | undo redo | bold italic underline strikethrough removeformat | forecolor backcolor | fontfamily fontsize | alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist | showcomments | charmap | topdf | fullscreen ';
  555. var tinymenu = 'file edit';
  556. var tinytable = 'tableinsertrowbefore tableinsertrowafter tabledeleterow';
  557. var tinycontextmenu = '';
  558. var tinytableappearance = false;
  559. var tinytableclass = [];
  560. var tinytableresizebars = false;
  561. var tinyadvtab = false;
  562. var tinyobjectresizing = false;
  563. var tinypasteplain = false;
  564. var tinypasteremovestyles = true;
  565. function tinyconfigure(editor){
  566. editor.on('keydown', function(e) {
  567. if (e.keyCode == 8 || e.keyCode == 46) { //backspace and delete keycodes
  568. try {
  569. var elem = tinymce.activeEditor.selection.getNode(); //current caret node
  570. console.log(elem);
  571. if (elem.classList.contains("mceNonEditable")) {
  572. e.preventDefault();
  573. return false;
  574. }
  575. } catch (e) {console.log(e);}
  576. }
  577. });
  578. editor.ui.registry.addIcon('save', '<i style="font-size:125%;" class="fas fa-save"></i>')
  579. editor.ui.registry.addButton('topdf', {
  580. icon: 'print',
  581. tooltip: 'Imprimir a PDF',
  582. onAction: function(_){
  583. print_to_pdf(editor.getContent());
  584. }
  585. });
  586. editor.on('FullscreenStateChanged', function () {
  587. if(tinymce.activeEditor.plugins.fullscreen.isFullscreen()){
  588. var e = '<span id="exitFullScreen" class="btn btn-primary pointer" style="position:absolute;display:none;float:right;right:15px;top:25px;color:white;background-color:#007bff;padding:8px;cursor:pointer;" onclick="tinymce.activeEditor.execCommand('+"'mceFullScreen'"+');">Volver a SAIC <i class="fas fa-compress-arrows-alt"></i></span>';
  589. $(e).appendTo($($('.tox-editor-container').get(0)));
  590. $('#exitFullScreen').show();
  591. }
  592. else{
  593. $('#exitFullScreen').remove();
  594. }
  595. });
  596. }
  597. </script>
  598. <script type="text/javascript">
  599. var locale = "[[${#locale.language}]]";
  600. var fileInvalid = "[[#{procedures.extensionAlert}]]";
  601. var fullname = "[[${#authentication.principal.nom + ' ' + #authentication.principal.cognoms}]]";
  602. var username = "[[${#authentication.principal.usuari}]]";
  603. var msgsave = "[[#{procedures.editor.msgsave}]]";
  604. var expiredTxt = "[[#{procedures.session.expired}]]";
  605. var layout;
  606. var lastActiveTime = new Date().getTime();
  607. var autosaveTriggered = false;
  608. mermaid.initialize({ startOnLoad: false, theme: "default", flowchart: { diagramPadding: 5, useMaxWidth:false, htmlLabels:true, wrap: true, wrappingWidth: 750 } });
  609. $(document).ready(function(){
  610. setAliveInterval();
  611. setSaveInterval();
  612. layout = new Layout("");
  613. layout.initTableLayout({paging:false});
  614. layout.closeLoadingSpinner(".uv-loading-spinner");
  615. $('#extensionAlert').hide();
  616. var extensions = ($('#activeOptions').val().replace('.','') === '') ? [] : $('#activeOptions').val().replace('.','').split(';');
  617. if(extensions.length < 1){
  618. extensions = ['pdf'];
  619. if (typeof typeActive !== 'undefined') {
  620. if(typeActive == 12 | typeActive == 14){
  621. extensions = ['pdf', 'doc', 'docx', 'odt'];
  622. }
  623. }
  624. }
  625. $("#filepicker").fileinput({
  626. uploadUrl: "/procedure/files/"+$('input[name=procid].active-task').val(),
  627. uploadExtraData: {taskid:$('input[name=taskid].active-task').val()},
  628. uploadAsync: true,
  629. maxAjaxThreads: 1,
  630. showUpload: false,
  631. required: true,
  632. browseOnZoneClick: true,
  633. language: locale,
  634. allowedFileExtensions: extensions,
  635. previewFileIcon: '<i class="fas fa-file"></i>',
  636. theme: 'fas',
  637. allowedPreviewTypes: null,
  638. removeFromPreviewOnError: true,
  639. initialPreviewShowDelete: true,
  640. msgFileRequired: locale == 'es'?'Debe seleccionar la evidencia':'Ha de seleccionar l\'evidència',
  641. previewFileIconSettings: {
  642. 'pdf': '<i class="fas fa-file-pdf text-danger"></i>',
  643. },
  644. fileActionSettings: {
  645. showRemove: true,
  646. showZoom: false,
  647. showUpload: false,
  648. showDrag: false,
  649. removeIcon: '<i class="fas fa-trash"></i>'
  650. }
  651. });
  652. $('#filepicker').on('filebatchuploadcomplete', function(event, data, previewId, index) {
  653. location.reload();
  654. });
  655. tinymce.init({
  656. selector: '#editor',
  657. language: locale,
  658. browser_spellcheck: true,
  659. plugins: tinyplugins,
  660. menubar: tinymenu,
  661. toolbar: tinytoolbar,
  662. table_toolbar: tinytable,
  663. tinycomments_author: username,
  664. tinycomments_author_name: fullname,
  665. tinycomments_mode: 'embedded',
  666. tinycomments_can_delete: function(req, done, fail){ done({canDelete: true}); },
  667. table_advtab: tinyadvtab,
  668. table_row_advtab: tinyadvtab,
  669. table_cell_advtab: tinyadvtab,
  670. table_sizing_mode: 'fixed',
  671. table_default_styles: {
  672. width: '297mm'
  673. },
  674. content_style: "body { width: 297mm; max-width: 297mm; font-size: 10pt; font-family: Verdana, Geneva, sans-serif; font-style: normal; font-variant: normal;}",
  675. font_formats: "Arial=arial,helvetica,sans-serif; Verdana=verdana,geneva;",
  676. font_size_formats: '8pt 10pt 12pt 14pt 16pt 18pt 24pt',
  677. setup: tinyconfigure,
  678. save_onsavecallback: function(){
  679. if(autosaveTriggered){
  680. save_content(false);
  681. autosaveTriggered = false;
  682. }
  683. else{
  684. save_content(true);
  685. }
  686. },
  687. content_langs: [
  688. {title: 'Español', code: 'es'},
  689. {title: 'Valencià', code: 'ca'}],
  690. image_advtab: true,
  691. importcss_append: true,
  692. height: 700,
  693. image_caption: true,
  694. quickbars_selection_toolbar: '',
  695. toolbar_mode: 'sliding',
  696. contextmenu: tinycontextmenu,
  697. table_appearance_options: tinytableappearance,
  698. table_class_list: tinytableclass,
  699. table_resize_bars: tinytableresizebars,
  700. verify_html: false,
  701. cleanup: false,
  702. valid_elements: '+*[*]',
  703. paste_as_text: tinypasteplain,
  704. paste_remove_styles: tinypasteremovestyles,
  705. paste_strip_class_attributes: true
  706. });
  707. $('[data-toggle=confirmation]').confirmation({
  708. rootSelector: '[data-toggle=confirmation]',
  709. container: 'body',
  710. btnOkClass: 'btn btn-sm btn-success',
  711. btnOkLabel: 'Sí',
  712. btnCancelClass: 'btn btn-sm btn-danger',
  713. btnCancelLabel: 'No',
  714. });
  715. if ($.fn.DataTable.isDataTable('#pamTable')) {
  716. $('#pamTable').DataTable().destroy();
  717. }
  718. $('#pamTable').DataTable({
  719. "searching": false,
  720. "ordering": false,
  721. "paging": false,
  722. "info": false,
  723. "responsive": true,
  724. "autoWidth": false
  725. });
  726. var jsonPrueba = [
  727. {"id":"RH2-1", "objetivo":"Implantar el Manual para la Evaluación de la actividad docente del profesorado(Programa DOCENTIA)", "origen":"SAIC / Informe Externo", "actividades":"Fases 1 a 6 de certificación", "responsable":"Unidad de Calidad / RRHH", "prioridad":"Alta", "cursoInicio":"Curso Inicio:2022", "cursoFin":"Curso fin: 2024", "indicadores":"Profesorado evaluado"," gradoEjecucion":"En desarrollo", "justificacion":"Faltan los trámites de la Fase 6."},
  728. {"id":"RH2-2","objetivo":null, "origen":null,"actividades":null,"responsable":null,"prioridad":"Alta","cursoInicio":null,"cursoFin":"27/28","indicadores":null,"gradoEjecucion":"En desarrollo","justificacion":null}];
  729. rebuildPAMTable(jsonPrueba);
  730. $('#pamTable').on('input', 'textarea', function() {
  731. autoResize(this);
  732. });
  733. if(widget) {
  734. dragElement(widget);
  735. widget.ondblclick = function() {
  736. togglePdfPreview();
  737. };
  738. }
  739. $('#pamTable tbody tr').each(function() {
  740. $('.curso-select-dinamico').html(generarOpcionesCursos());
  741. });
  742. $('#pamTable').on('dblclick', 'td textarea', function() {
  743. const target = this;
  744. const currentVal = $(target).val();
  745. const editorHtml = `
  746. <div class="p-2" style="width: 400px;">
  747. <label class="small font-weight-bold">Editor rápido</label>
  748. <textarea id="popoverEditor" class="form-control mb-2" rows="6">${currentVal}</textarea>
  749. <div class="text-right">
  750. <button type="button" id="cancelPop" class="btn btn-xs btn-light">Cancelar</button>
  751. <button type="button" id="savePop" class="btn btn-xs btn-primary">Guardar</button>
  752. </div>
  753. </div>
  754. `;
  755. const instance = tippy(target, {
  756. content: editorHtml,
  757. allowHTML: true,
  758. interactive: true,
  759. trigger: 'manual',
  760. placement: 'top',
  761. theme: 'light',
  762. maxWidth: 'none',
  763. onShown(instance) {
  764. $('#popoverEditor').focus();
  765. $('#savePop').on('click', (e) => {
  766. e.preventDefault();
  767. e.stopPropagation();
  768. const newVal = $('#popoverEditor').val();
  769. $(target).val(newVal);
  770. autoResize(target);
  771. instance.hide();
  772. });
  773. $('#cancelPop').on('click', (e) => {
  774. e.preventDefault();
  775. e.stopPropagation();
  776. instance.hide();
  777. });
  778. },
  779. onHidden(instance) {
  780. instance.destroy();
  781. }
  782. });
  783. instance.show();
  784. });
  785. });
  786. $('#customFile').on("change", function() {
  787. var fileName = $(this).val().split("\\").pop();
  788. $(this).siblings(".custom-file-label").addClass("selected").html(fileName);
  789. if (fileName.split('.').pop().toLowerCase() == "pdf"){
  790. $('#custom-file-label').removeClass('file-invalid');
  791. }
  792. else{
  793. $('#custom-file-label').text(fileInvalid)
  794. $('#custom-file-label').addClass('file-invalid');
  795. $('#customFile').val("");
  796. }
  797. });
  798. function openProcedure(id){
  799. window.location.replace('/procedure/'+id);
  800. }
  801. function click_y(){
  802. $('#action_n').removeClass('btn-n-active');
  803. $('#action_y').addClass('btn-y-active');
  804. $('#response').val(1);
  805. }
  806. function click_n(){
  807. $('#action_y').removeClass('btn-y-active');
  808. $('#action_n').addClass('btn-n-active');
  809. $('#response').val(0);
  810. }
  811. function triggerSave(){
  812. autosaveTriggered = true;
  813. tinymce.activeEditor.execCommand('mceSave');
  814. console.log('Autosave triggered');
  815. }
  816. function keepalive(){
  817. $.get("/keepalive")
  818. .done(function(data){
  819. if(data != 'alive'){
  820. alert(expiredTxt.replaceAll("&#39;", "'"));
  821. saveWorker.terminate();
  822. aliveWorker.terminate();
  823. window.location.reload();
  824. }
  825. })
  826. .fail(function() {
  827. alert(expiredTxt.replaceAll("&#39;", "'"));
  828. saveWorker.terminate();
  829. aliveWorker.terminate();
  830. window.location.reload();
  831. });
  832. }
  833. function saveEditorWithConfirmation(){
  834. tinymce.activeEditor.setDirty(true);
  835. tinymce.activeEditor.execCommand('mceSave');
  836. alert(msgsave);
  837. }
  838. function createFilepicker(procId, taskId){
  839. $('.additional-file-container').empty();
  840. $('#additionalFileContainer'+taskId).html('<input class="additional-file-input" id="filepicker'+taskId+'" multiple type="file" name="evidencias">'
  841. +'<br><span class="btn btn-success pointer" onclick="$(\'#filepicker'+taskId+'\').fileinput(\'upload\');">Confirmar</span><br><br><br>');
  842. $("#filepicker"+taskId).fileinput({
  843. uploadUrl: "/procedure/files/"+procId,
  844. uploadExtraData: {taskid:taskId},
  845. uploadAsync: true,
  846. showUpload: true,
  847. required: true,
  848. browseOnZoneClick: true,
  849. language: locale,
  850. allowedFileExtensions: ['pdf'],
  851. previewFileIcon: '<i class="fas fa-file"></i>',
  852. theme: 'fas',
  853. allowedPreviewTypes: null,
  854. removeFromPreviewOnError: true,
  855. initialPreviewShowDelete: true,
  856. msgFileRequired: locale == 'es'?'Debe seleccionar la evidencia':'Ha de seleccionar l\'evidència',
  857. previewFileIconSettings: {
  858. 'pdf': '<i class="fas fa-file-pdf text-danger"></i>',
  859. },
  860. fileActionSettings: {
  861. showRemove: true,
  862. showZoom: false,
  863. showUpload: false,
  864. showDrag: false,
  865. removeIcon: '<i class="fas fa-trash"></i>'
  866. }
  867. });
  868. $('#filepicker'+taskId).on('filebatchuploadsuccess', function(event, data) {
  869. var form = data.form, files = data.files, extra = data.extra,
  870. response = data.response, reader = data.reader;
  871. window.location.href = window.location.href;
  872. });
  873. }
  874. function save_content(manualDraftSave){
  875. $.post("/procedure/save/"+idInstanciaTasca,
  876. {text:tinymce.activeEditor.getContent(),
  877. manual: manualDraftSave
  878. })
  879. .done(function(d){
  880. $('#lastmoddate').text(d);
  881. console.log('content saved at'+d);
  882. });
  883. }
  884. function print_to_pdf(content){
  885. $('<form id="tmpDownloadForm" method="post" action="/download/pdf/preview" target="_blank">' +
  886. '<input type="text" name="idtascai" value="'+$('input[name="taskid"]').val()+'" />' +
  887. '<textarea id="tmpDownloadContent" type="hidden" name="content">' +
  888. tinymce.activeEditor.getContent() +
  889. '</textarea>' +
  890. '</form>')
  891. .appendTo('body')
  892. .submit()
  893. .remove();
  894. }
  895. function showComments(){
  896. setTimeout(function(){
  897. if(typeActive == 15){
  898. tinymce.activeEditor.execCommand("ToggleSidebar", false, "showcomments");
  899. }
  900. }, 700);
  901. }
  902. function target_popup(form) {
  903. //window.open('', 'formpopup', 'width=1350,height=800,resizeable,scrollbars');
  904. window.open('', 'formpopup', 'width=1350,height=800,resizeable');
  905. form.target = 'formpopup';
  906. }
  907. function showFlowDiagram(){
  908. if($('#flowDiagramContainer').is(":visible")){
  909. hideFlowDiagram();
  910. }
  911. else{
  912. $('#flowDiagramContainer').show();
  913. mermaid.run({querySelector:'.mermaid'});
  914. }
  915. }
  916. function hideFlowDiagram(){
  917. $('#flowDiagramContainer').hide();
  918. }
  919. function downloadFlowDiagram() {
  920. const svgElement = document.querySelector('svg[aria-roledescription="flowchart-v2"]');
  921. const filename = 'flujo_procedimiento_'+$('#procedure_code').text();
  922. if(!svgElement) {
  923. console.error('No se encontró ningún SVG.');
  924. return;
  925. }
  926. const clonedSvg = svgElement.cloneNode(true);
  927. const width = parseInt(clonedSvg.getAttribute('width') || 800);
  928. const height = parseInt(clonedSvg.getAttribute('height') || 600);
  929. // Embebemos los estilos de Mermaid en el SVG
  930. const style = document.createElement('style');
  931. style.textContent = Array.from(document.styleSheets)
  932. .filter(s => !s.href || s.href.startsWith(window.location.origin))
  933. .map(sheet => {
  934. try {
  935. return Array.from(sheet.cssRules).map(rule => rule.cssText).join('\n');
  936. } catch (e) {
  937. return '';
  938. }
  939. })
  940. .join('\n');
  941. clonedSvg.insertBefore(style, clonedSvg.firstChild);
  942. const svgData = new XMLSerializer().serializeToString(clonedSvg);
  943. const svgBase64 = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svgData)));
  944. const img = new Image();
  945. img.onload = function () {
  946. const canvas = document.createElement('canvas');
  947. canvas.width = width;
  948. canvas.height = height;
  949. const ctx = canvas.getContext('2d');
  950. ctx.fillStyle = '#ffffff';
  951. ctx.fillRect(0, 0, canvas.width, canvas.height);
  952. ctx.drawImage(img, 0, 0);
  953. const pngUrl = canvas.toDataURL('image/png');
  954. const link = document.createElement('a');
  955. link.href = pngUrl;
  956. link.download = filename;
  957. document.body.appendChild(link);
  958. link.click();
  959. document.body.removeChild(link);
  960. };
  961. img.src = svgBase64;
  962. }
  963. function addNewRowPAM() {
  964. const tableBody = document.querySelector('#pamTable tbody');
  965. const filas = tableBody.querySelectorAll('tr');
  966. let nuevoId ="RH-1";
  967. if (filas.length > 0) {}{
  968. const ultimaFila = filas[filas.length - 1];
  969. const ultimoIdTexto = ultimaFila.querySelector('td').innerText.trim();
  970. const partes = ultimoIdTexto.match(/^(.*-)(\d+)$/);
  971. if (partes) {
  972. const prefijo = partes[1];
  973. const numero = parseInt(partes[2]) + 1;
  974. nuevoId = prefijo + numero.toString().padStart(2, '0');
  975. }
  976. }
  977. const newRow = document.createElement('tr');
  978. const opcionesDinamicas = generarOpcionesCursos();
  979. newRow.innerHTML = `
  980. <td><small>${nuevoId}</small></td>
  981. <td><textarea class="form-control form-control-sm" rows="3" placeholder="Objetivo..."></textarea></td>
  982. <td>
  983. <select class="form-control form-control-sm">
  984. <option>SAIC</option>
  985. <option>Autoinforme</option>
  986. <option>Informe Externo</option>
  987. </select>
  988. </td>
  989. <td><textarea class="form-control form-control-sm" rows="3" placeholder="Actividades..."></textarea></td>
  990. <td><textarea class="form-control form-control-sm" rows="2" placeholder="Responsable..."></textarea></td>
  991. <td>
  992. <select class="form-control form-control-sm">
  993. <option>Alta</option>
  994. <option>Media</option>
  995. <option>Baja</option>
  996. </select>
  997. </td>
  998. <td><select class="form-control form-control-sm">${opcionesDinamicas}</select></td>
  999. <td><select class="form-control form-control-sm">${opcionesDinamicas}</select></td>
  1000. <td><textarea class="form-control form-control-sm" rows="2" placeholder="Indicadores"></textarea></td>
  1001. <td>
  1002. <select class="form-control form-control-sm">
  1003. <option>Planificación</option>
  1004. <option>En desarrollo</option>
  1005. <option>Finalizado</option>
  1006. <option>Cancelada</option>
  1007. </select>
  1008. </td>
  1009. <td><textarea class="form-control form-control-sm" rows="2" placeholder="Justificación"></textarea></td>
  1010. `;
  1011. tableBody.appendChild(newRow);
  1012. newRow.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
  1013. }
  1014. function autoResize(textarea) {
  1015. textarea.style.height = 'auto';
  1016. textarea.style.height = textarea.scrollHeight + 'px';
  1017. }
  1018. function dragElement(elmnt) {
  1019. let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
  1020. elmnt.onmousedown = dragMouseDown;
  1021. function dragMouseDown(e) {
  1022. e = e || window.event;
  1023. pos3 = e.clientX;
  1024. pos4 = e.clientY;
  1025. document.onmouseup = closeDragElement;
  1026. document.onmousemove = elementDrag;
  1027. }
  1028. function elementDrag(e) {
  1029. e = e || window.event;
  1030. e.preventDefault();
  1031. pos1 = pos3 - e.clientX;
  1032. pos2 = pos4 - e.clientY;
  1033. pos3 = e.clientX;
  1034. pos4 = e.clientY;
  1035. elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
  1036. elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
  1037. elmnt.style.right = 'auto';
  1038. }
  1039. function closeDragElement() {
  1040. document.onmouseup = null;
  1041. document.onmousemove = null;
  1042. }
  1043. }
  1044. function togglePdfPreview() {
  1045. const $preview = $("#pdfPreviewContainer");
  1046. const $widget = $("#pdfWidget");
  1047. const $tableContainer = $('.table-responsive');
  1048. const $table = $('#pamTable');
  1049. if ($preview.is(":hidden")) {
  1050. const isRight = $widget.offset().left > (window.innerWidth / 2);
  1051. $widget.fadeOut(200);
  1052. $preview.addClass('active-split');
  1053. if (isRight) {
  1054. $preview.css({ left: 'auto', right: '0' });
  1055. $tableContainer.addClass('table-split-right').removeClass('table-split-left');
  1056. } else {
  1057. $preview.css({ left: '0', right: 'auto' });
  1058. $tableContainer.addClass('table-split-left').removeClass('table-split-right');
  1059. }
  1060. $table.addClass('compact-mode');
  1061. $preview.fadeIn(300).css("display", "flex");
  1062. setTimeout(function() {
  1063. if ($.fn.DataTable.isDataTable('#pamTable')) {
  1064. $('#pamTable').DataTable().columns.adjust();
  1065. }
  1066. }, 310);
  1067. } else {
  1068. closePdfPreview();
  1069. }
  1070. }
  1071. function closePdfPreview(event) {
  1072. if(event) {
  1073. event.preventDefault();
  1074. event.stopPropagation();
  1075. }
  1076. const $preview = $("#pdfPreviewContainer");
  1077. const $widget = $("#pdfWidget");
  1078. const $tableContainer = $('.table-responsive');
  1079. const $table = $('#pamTable');
  1080. $tableContainer.removeClass('table-split-right table-split-left');
  1081. $table.removeClass('compact-mode');
  1082. $preview.removeClass('active-split');
  1083. $preview.fadeOut(200, function() {
  1084. $widget.fadeIn(300);
  1085. });
  1086. }
  1087. function generarOpcionesCursos() {
  1088. const fechaActual = new Date();
  1089. const añoActual = fechaActual.getFullYear();
  1090. const mesActual = fechaActual.getMonth();
  1091. let cursoActual = (mesActual >= 7) ? añoActual : añoActual - 1;
  1092. let html = '<option value="">Seleccionar...</option>';
  1093. for (let i = -3; i <= 3; i++) {
  1094. let inicio = cursoActual + i;
  1095. let fin = inicio + 1;
  1096. let texto = `${inicio.toString().slice(-2)}/${fin.toString().slice(-2)}`;
  1097. html += `<option value="${texto}">${texto}</option>`;
  1098. }
  1099. return html;
  1100. }
  1101. function rebuildPAMTable(data) {
  1102. const tableBody = $('#pamTable tbody');
  1103. tableBody.empty();
  1104. if (!data || data.length === 0) return;
  1105. const renderInputOrStatic = function(value, placeholder, rows = 2) {
  1106. if (value === null || value === undefined || value === "" || value === "null") {
  1107. return `<textarea class="form-control form-control-sm" rows="${rows}" placeholder="${placeholder}"></textarea>`;
  1108. } else {
  1109. return `<small class="text-dark">${value}</small>`;
  1110. }
  1111. };
  1112. data.forEach(item => {
  1113. const row = $('<tr></tr>');
  1114. row.html(`
  1115. <td>
  1116. <small>${item.id || ''}</small>
  1117. </td>
  1118. <td>${renderInputOrStatic(item.objetivo, "Objetivo...", 3)}</td>
  1119. <td>
  1120. <select class="form-control form-control-sm">
  1121. <option value="SAIC" ${item.origen === 'SAIC' ? 'selected' : ''}>SAIC</option>
  1122. <option value="Autoinforme" ${item.origen === 'Autoinforme' ? 'selected' : ''}>Autoinforme</option>
  1123. <option value="Informe Externo" ${item.origen === 'Informe Externo' ? 'selected' : ''}>Informe Externo</option>
  1124. </select>
  1125. </td>
  1126. <td>${renderInputOrStatic(item.actividades, "Actividades...", 3)}</td>
  1127. <td>${renderInputOrStatic(item.responsable, "Responsable...")}</td>
  1128. <td>
  1129. <select class="form-control form-control-sm">
  1130. <option value="Alta" ${item.prioridad === 'Alta' ? 'selected' : ''}>Alta</option>
  1131. <option value="Media" ${item.prioridad === 'Media' ? 'selected' : ''}>Media</option>
  1132. <option value="Baja" ${item.prioridad === 'Baja' ? 'selected' : ''}>Baja</option>
  1133. </select>
  1134. </td>
  1135. <td><select class="form-control form-control-sm curso-inicio-val">${generarOpcionesCursos()}</select></td>
  1136. <td><select class="form-control form-control-sm curso-fin-val">${generarOpcionesCursos()}</select></td>
  1137. <td>${renderInputOrStatic(item.indicadores, "Indicadores...")}</td>
  1138. <td>
  1139. <select class="form-control form-control-sm">
  1140. <option value="Planificación" ${item.gradoEjecucion === 'Planificación' ? 'selected' : ''}>Planificación</option>
  1141. <option value="En desarrollo" ${item.gradoEjecucion === 'En desarrollo' ? 'selected' : ''}>En desarrollo</option>
  1142. <option value="Finalizado" ${item.gradoEjecucion === 'Finalizado' ? 'selected' : ''}>Finalizado</option>
  1143. <option value="Cancelada" ${item.gradoEjecucion === 'Cancelada' ? 'selected' : ''}>Cancelada</option>
  1144. </select>
  1145. </td>
  1146. <td>${renderInputOrStatic(item.justificacion, "Justificación...")}</td>
  1147. `);
  1148. tableBody.append(row);
  1149. });
  1150. tableBody.find('textarea').each(function() {
  1151. if (typeof autoResize === 'function') autoResize(this);
  1152. });
  1153. }
  1154. function processPAMTable(actionType) {
  1155. var data = [];
  1156. var rows = $('#pamTable tbody tr');
  1157. rows.each(function() {
  1158. var row = $(this);
  1159. var getVal = function(cellIndex) {
  1160. var cell = row.find('td').eq(cellIndex);
  1161. var input = cell.find('textarea, select, input');
  1162. if (input.length > 0) {
  1163. return input.val();
  1164. } else {
  1165. return cell.text().trim();
  1166. }
  1167. };
  1168. var rowData = {
  1169. "id": getVal(0),
  1170. "objetivo": getVal(1),
  1171. "origen": getVal(2),
  1172. "actividades": getVal(3),
  1173. "responsable": getVal(4),
  1174. "prioridad": getVal(5),
  1175. "cursoInicio": getVal(6),
  1176. "cursoFin": getVal(7),
  1177. "indicadores": getVal(8),
  1178. "gradoEjecucion": getVal(9),
  1179. "justificacion": getVal(10)
  1180. };
  1181. if (rowData.id !== "") {
  1182. data.push(rowData);
  1183. }
  1184. });
  1185. var jsonResult = JSON.stringify(data);
  1186. $('#pam_json_data').val(jsonResult);
  1187. if (actionType === 'draft') {
  1188. console.log("Generando Borrador JSON:", jsonResult);
  1189. alert("Borrador de la tabla generado correctamente en el sistema.");
  1190. } else {
  1191. if (data.length === 0) {
  1192. alert("La tabla está vacía. Por favor, añade al menos una acción.");
  1193. return false;
  1194. }
  1195. console.log("Enviando JSON definitivo:", jsonResult);
  1196. }
  1197. }
  1198. </script>
  1199. </body>
  1200. </html>