procedure.html 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905
  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="${#authentication.principal.isAdmin() or #authentication.principal.isGranted()}" 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 #authentication.principal.isAdmin() or #authentication.principal.isGranted() ? '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 #authentication.principal.isAdmin() or #authentication.principal.isGranted()}"><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="${#authentication.principal.isAdmin() or #authentication.principal.isGranted()}" 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 #authentication.principal.isAdmin() or #authentication.principal.isGranted()}">
  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="row card-body-up-btn">
  218. <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>
  219. <button class="btn btn-success" th:if="${not #lists.contains({1, 11, 12, 13, 14, 15}, item.tipus)}">Confirmar</button>
  220. <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>
  221. <span class="btn btn-success pointer" th:if="${#lists.contains({1, 12, 13, 14}, item.tipus)}" onclick="$('#filepicker').fileinput('upload');">Confirmar</span>
  222. </div>
  223. </form>
  224. </div>
  225. </div>
  226. <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())}">
  227. <!-- Añadir más evidencias (solo uq y admin) -->
  228. <form class="col" enctype='multipart/form-data' method="POST" th:action="${'/procedure/'+instance.idInstancia}">
  229. <input type="hidden" name="taskid" th:value="${item.idInstanciaTasca}">
  230. <input type="hidden" name="procid" th:value="${instance.idInstancia}">
  231. <div class="additional-file-container" th:attr="id='additionalFileContainer'+${item.idInstanciaTasca}"></div>
  232. </form>
  233. </div>
  234. <div class="row" th:unless="${item.estat == 'A'}">
  235. <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())}">
  236. <input type="hidden" name="idInstanciaTasca" th:value="${item.idInstanciaTasca}">
  237. <div class="btn-toolbar" role="toolbar" style="margin-top:20px;">
  238. <div class="btn-group w-auto ml-auto" role="group" aria-label="First group" style="margin-left:0px;">
  239. <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>
  240. </div>
  241. </div>
  242. </form>
  243. <span style="text-align:left;" th:if="${item.estat != null}">
  244. <span th:if="${item.tipus == 4}"> <!-- avanzar -->
  245. <span th:text="#{procedures.done.response}"></span>: <span th:text="#{procedures.status.f}"></span>
  246. </span>
  247. <span th:if="${item.tipus == 2 and item.estat == 'S'}"> <!-- pregunta resp si -->
  248. <span th:text="#{procedures.done.response}"></span>: <span th:text="#{procedures.done.response.s}"></span>
  249. </span>
  250. <span th:if="${item.tipus == 2 and item.estat == 'N'}"> <!-- pregunta resp no -->
  251. <span th:text="#{procedures.done.response}"></span>: <span th:text="#{procedures.done.response.n}"></span>
  252. </span>
  253. <span th:if="${#lists.contains({1, 11, 12, 13, 14, 15, 21, 22}, item.tipus)}"> <!-- evidencia -->
  254. <span th:text="#{procedures.done.evidence}"></span>:
  255. <a th:href="${'/download/'+item.idInstanciaTasca}" target="_blank">
  256. <strong th:text="${item.codiEvidencia}"></strong> <span th:text="${#locale.language} == 'es' ? ${item.nomEvidenciaCas}:${item.nomEvidenciaVal}"></span>
  257. <i class="fas fa-file-pdf"></i>
  258. </a>
  259. </span>
  260. <br>
  261. <span><span th:text="#{procedures.done.user}"></span>: <span th:text="${item.usuariFet}"></span></span>
  262. <br>
  263. <span><span th:text="#{procedures.done.date}"></span>: <span th:text="${item.dataFet}"></span></span>
  264. </span>
  265. </div>
  266. <div class="container" th:if="${item.versions != null}" style="margin-top:25px;text-align:left;margin-left: 0px;">
  267. <strong th:if="${#lists.size(item.versions) > 0}" th:text="#{procedures.otherActions}">Acciones realizadas anteriormente</strong>
  268. <br>
  269. <div class="row" style="text-align:left;" th:if="${#lists.size(item.versions) > 0}" th:each="version,i : ${item.versions}">
  270. <hr class="uv-procedure-hr" style="width:100%;text-align:left;margin-left: 0px;">
  271. <div class="col-1" th:if="${#authentication.principal.isAdmin() or #authentication.principal.isGranted()}" style="flex:0 0 0;width:50px !important;padding:0;">
  272. <form enctype='multipart/form-data' method="POST" action="/admin/instance/task/remove" style="display: inline;">
  273. <input type="hidden" name="idInstanciaTasca" th:value="${version.idInstanciaTasca}">
  274. <div class="btn-toolbar" role="toolbar" style="margin-top:20px;">
  275. <div class="btn-group w-auto ml-auto" role="group" aria-label="First group" style="margin-left:0px;">
  276. <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>
  277. </div>
  278. </div>
  279. </form>
  280. </div>
  281. <div class="col-11">
  282. <span th:if="${version.tipus == 4}"> <!-- avanzar -->
  283. <span th:text="#{procedures.done.response}"></span>: <span th:text="#{procedures.status.f}"></span>
  284. </span>
  285. <span th:if="${version.tipus == 2 and version.estat == 'S'}"> <!-- pregunta resp si -->
  286. <span th:text="#{procedures.done.response}"></span>: <span th:text="#{procedures.done.response.s}"></span>
  287. </span>
  288. <span th:if="${version.tipus == 2 and version.estat == 'N'}"> <!-- pregunta resp no -->
  289. <span th:text="#{procedures.done.response}"></span>: <span th:text="#{procedures.done.response.n}"></span>
  290. </span>
  291. <span th:if="${version.tipus == 1 or version.tipus == 11 or version.tipus == 15}"> <!-- evidencia -->
  292. <span th:text="#{procedures.done.evidence}"></span>:
  293. <a th:href="${'/download/'+version.idInstanciaTasca}" target="_blank">
  294. <strong th:text="${version.codiEvidencia}"></strong> <span th:text="${#locale.language} == 'es' ? ${version.nomEvidenciaCas}:${version.nomEvidenciaVal}"></span>
  295. <i class="fas fa-file-pdf"></i>
  296. </a>
  297. </span>
  298. <br>
  299. <span><span th:text="#{procedures.done.user}"></span>: <span th:text="${version.usuariFet}"></span></span>
  300. <br>
  301. <span><span th:text="#{procedures.done.date}"></span>: <span th:text="${version.dataFet}"></span></span>
  302. </div>
  303. <br>
  304. </div>
  305. </div>
  306. </div>
  307. </div>
  308. </div>
  309. </div>
  310. </div>
  311. <div class="uv-table-group procedure-status" th:if="${instance.estat == 'F'}">
  312. <strong th:text="#{procedures.closed}"></strong>
  313. </div>
  314. <div class="uv-table-group procedure-status" th:if="${instance.estat == 'C'}">
  315. <strong th:text="#{procedures.cancelled}"></strong>
  316. </div>
  317. <div class="uv-table-group procedure-status" th:if="${location == 'supervision'}">
  318. <a href="/supervision/search" class="btn btn-primary pointer" th:text="#{procedures.back}">Volver</a>
  319. </div>
  320. </div>
  321. </div>
  322. </div>
  323. <div class="modal fade" id="flowDiagramModal" tabindex="-1" role="dialog" aria-labelledby="flowDiagramModal" aria-hidden="true">
  324. <div class="modal-dialog modal-xl" >
  325. <div class="modal-content">
  326. <div class="modal-header">
  327. <h5 class="modal-title">Flujo del procedimiento</h5>
  328. <button type="button" class="close" data-dismiss="modal" aria-label="Close">
  329. <span aria-hidden="true">&times;</span>
  330. </button>
  331. </div>
  332. <div class="modal-body">
  333. <div class="mermaid-def" th:text="${flow}" style="display:none;"></div>
  334. <div id="flowDiagram" class="mermaid"></div>
  335. </div>
  336. <div class="modal-footer">
  337. <button type="button" class="btn btn-secondary" data-dismiss="modal" th:text="#{global.cancel}">Cerrar</button>
  338. </div>
  339. </div>
  340. </div>
  341. </div>
  342. <!-- contactModal -->
  343. <div th:replace="~{layouts/common.html :: contactModal}"></div>
  344. <!-- Footer -->
  345. <footer class="uv-footer uv-footer-text" th:replace="~{layouts/common.html :: footer}"></footer>
  346. <script th:src="@{/js/jquery/jquery.min.js}"></script>
  347. <script th:src="@{/js/bootstrap/bootstrap.min.js}"></script>
  348. <script th:src="@{/js/fa/all.js}"></script>
  349. <script th:src="@{/js/jquery/jquery.dataTables.min.js}"></script>
  350. <script th:src="@{/js/jquery/jquery.dataTables.responsive.min.js}"></script>
  351. <script th:src="@{/js/jquery/jquery.dataTables.rowReorder.min.js}"></script>
  352. <script th:src="@{/js/bootstrap/dataTables.bootstrap4.min.js}"></script>
  353. <script th:src="@{/js/jquery-easing/jquery.easing.min.js}"></script>
  354. <script th:src="@{/js/saic.js}"></script>
  355. <script th:src="@{/js/datepicker/bootstrap-datepicker.min.js}"></script>
  356. <script th:src="@{/js/fileinput/js/fileinput.min.js}"></script>
  357. <script th:src="@{/js/fileinput/themes/fas/theme.js}"></script>
  358. <script th:src="@{/js/fileinput/js/locales/es.js}" th:if="${#locale.language == 'es'}"></script>
  359. <script th:src="@{/js/fileinput/js/locales/ca.js}" th:if="${#locale.language != 'es'}"></script>
  360. <script th:src="@{/js/confirmation/bootstrap-confirmation.js}"></script>
  361. <script src="https://cdn.jsdelivr.net/npm/mermaid@11.3.0/dist/mermaid.min.js"></script>
  362. <script th:inline="javascript" th:if="${#authentication.principal.isAdmin()}">
  363. var tinyobjectresizing = true;
  364. </script>
  365. <script th:inline="javascript" th:if="${#authentication.principal.isAdmin() or #authentication.principal.isGranted()}">
  366. if (!window.Worker) {
  367. alert(/*[[#{procedures.nosupported}]]*/);
  368. window.location = '/';
  369. }
  370. var aliveWorker;
  371. var saveWorker;
  372. function confirmDelete(){ }
  373. function confirmClear(){ }
  374. function setSaveInterval(){ console.log('Autosave disabled'); }
  375. function setAliveInterval(){
  376. console.log('KeepAlive enabled');
  377. aliveWorker = new Worker(/*[[@{/js/workers/alive_worker.js}]]*/);
  378. aliveWorker.onmessage = keepalive;
  379. }
  380. 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';
  381. 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 ';
  382. var tinymenu = 'file edit view insert format tools table help';
  383. var tinytable = 'table tabledelete | tableprops tablerowprops tablecellprops | tableinsertrowbefore tableinsertrowafter tabledeleterow | tableinsertcolbefore tableinsertcolafter tabledeletecol';
  384. var tinycontextmenu = 'link table useBrowserSpellcheck';
  385. var tinytableappearance = true;
  386. var tinytableclass = [
  387. {title: 'Normal', value: ''},
  388. {title: 'Editable', value: 'mceEditable'},
  389. {title: 'No editable', value: 'mceNonEditable'},
  390. {title: 'Ignorar en el PDF', value: 'mceNonEditable pdfignore'}
  391. ];
  392. var tinytableresizebars = true;
  393. var tinyadvtab = true;
  394. var tinyobjectresizing = false;
  395. var tinypasteplain = false;
  396. var tinypasteremovestyles = true;
  397. function tinyconfigure(editor){
  398. editor.on('keydown', function(e) {
  399. if (e.keyCode == 8 || e.keyCode == 46) { //backspace and delete keycodes
  400. try {
  401. var elem = tinymce.activeEditor.selection.getNode(); //current caret node
  402. if (elem.classList.contains("mceNonEditable")) {
  403. e.preventDefault();
  404. return false;
  405. }
  406. } catch (e) {console.log(e);}
  407. }
  408. });
  409. editor.on('TableModified', function(e) {
  410. editor.setContent(editor.getContent());
  411. });
  412. editor.ui.registry.addIcon('save', '<i style="font-size:125%;" class="fas fa-save"></i>')
  413. editor.ui.registry.addButton('topdf', {
  414. icon: 'print',
  415. tooltip: 'Imprimir a PDF',
  416. onAction: function(_){
  417. print_to_pdf(editor.getContent());
  418. }
  419. });
  420. editor.on('FullscreenStateChanged', function () {
  421. if(tinymce.activeEditor.plugins.fullscreen.isFullscreen()){
  422. 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>';
  423. $(e).appendTo($($('.tox-editor-container').get(0)));
  424. $('#exitFullScreen').show();
  425. }
  426. else{
  427. $('#exitFullScreen').remove();
  428. }
  429. });
  430. editor.ui.registry.addButton('btn-editable', {
  431. icon: 'unlock',
  432. tooltip: 'Desbloquear contenido',
  433. onAction: function(_){
  434. var ns = $(editor.selection.getSelectedBlocks()).each(function(b){
  435. $(this).removeClass('mceEditable').removeClass('mceNonEditable').addClass('mceEditable')
  436. });
  437. editor.setContent(editor.getContent());
  438. }
  439. });
  440. editor.ui.registry.addButton('btn-noneditable', {
  441. icon: 'lock',
  442. tooltip: 'Bloquear contenido',
  443. onAction: function(_){
  444. var ns = $(editor.selection.getSelectedBlocks()).each(function(b){
  445. $(this).removeClass('mceEditable').removeClass('mceNonEditable').addClass('mceNonEditable')
  446. });
  447. editor.setContent(editor.getContent());
  448. }
  449. });
  450. editor.ui.registry.addMenuItem("useBrowserSpellcheck", {
  451. text: "Use `Ctrl + Click derecho` para acceder al corrector",
  452. onAction: function () {
  453. editor.notificationManager.open({
  454. text:"Para usar el corrector, mantenga pulsada la tecla Control (Ctrl) y haga click derecho sobre la palabra.",
  455. type: "info",
  456. timeout: 5000,
  457. closeButton: true
  458. });
  459. }
  460. });
  461. editor.ui.registry.addContextMenu("useBrowserSpellcheck", {
  462. update: function (node) {
  463. return editor.selection.isCollapsed() ? ["useBrowserSpellcheck"] : [];
  464. }
  465. });
  466. }
  467. </script>
  468. <script th:inline="javascript" th:unless="${#authentication.principal.isAdmin() or #authentication.principal.isGranted()}">
  469. if (!window.Worker) {
  470. alert(/*[[#{procedures.nosupported}]]*/);
  471. window.location = '/';
  472. }
  473. var aliveWorker;
  474. var saveWorker;
  475. function confirmDelete(){ }
  476. function confirmClear(){ }
  477. function setSaveInterval(){
  478. console.log('AutoSave enabled');
  479. saveWorker = new Worker(/*[[@{/js/workers/autosave_worker.js}]]*/);
  480. saveWorker.onmessage = triggerSave;
  481. }
  482. function setAliveInterval(){
  483. console.log('KeepAlive enabled');
  484. aliveWorker = new Worker(/*[[@{/js/workers/alive_worker.js}]]*/);
  485. aliveWorker.onmessage = keepalive;
  486. }
  487. 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';
  488. 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 ';
  489. var tinymenu = 'file edit';
  490. var tinytable = 'tableinsertrowbefore tableinsertrowafter tabledeleterow';
  491. var tinycontextmenu = '';
  492. var tinytableappearance = false;
  493. var tinytableclass = [];
  494. var tinytableresizebars = false;
  495. var tinyadvtab = false;
  496. var tinyobjectresizing = false;
  497. var tinypasteplain = false;
  498. var tinypasteremovestyles = true;
  499. function tinyconfigure(editor){
  500. editor.on('keydown', function(e) {
  501. if (e.keyCode == 8 || e.keyCode == 46) { //backspace and delete keycodes
  502. try {
  503. var elem = tinymce.activeEditor.selection.getNode(); //current caret node
  504. console.log(elem);
  505. if (elem.classList.contains("mceNonEditable")) {
  506. e.preventDefault();
  507. return false;
  508. }
  509. } catch (e) {console.log(e);}
  510. }
  511. });
  512. editor.ui.registry.addIcon('save', '<i style="font-size:125%;" class="fas fa-save"></i>')
  513. editor.ui.registry.addButton('topdf', {
  514. icon: 'print',
  515. tooltip: 'Imprimir a PDF',
  516. onAction: function(_){
  517. print_to_pdf(editor.getContent());
  518. }
  519. });
  520. editor.on('FullscreenStateChanged', function () {
  521. if(tinymce.activeEditor.plugins.fullscreen.isFullscreen()){
  522. 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>';
  523. $(e).appendTo($($('.tox-editor-container').get(0)));
  524. $('#exitFullScreen').show();
  525. }
  526. else{
  527. $('#exitFullScreen').remove();
  528. }
  529. });
  530. }
  531. </script>
  532. <script type="text/javascript">
  533. var locale = "[[${#locale.language}]]";
  534. var fileInvalid = "[[#{procedures.extensionAlert}]]";
  535. var fullname = "[[${#authentication.principal.nom + ' ' + #authentication.principal.cognoms}]]";
  536. var username = "[[${#authentication.principal.usuari}]]";
  537. var msgsave = "[[#{procedures.editor.msgsave}]]";
  538. var expiredTxt = "[[#{procedures.session.expired}]]";
  539. var layout;
  540. var lastActiveTime = new Date().getTime();
  541. var autosaveTriggered = false;
  542. mermaid.initialize({ startOnLoad: false, theme: "default", flowchart: { diagramPadding: 5, useMaxWidth:false, htmlLabels:true, wrap: true, wrappingWidth: 750 } });
  543. $(document).ready(function(){
  544. setAliveInterval();
  545. setSaveInterval();
  546. layout = new Layout("");
  547. layout.initTableLayout({paging:false});
  548. layout.closeLoadingSpinner(".uv-loading-spinner");
  549. $('#extensionAlert').hide();
  550. var extensions = ($('#activeOptions').val().replace('.','') === '') ? [] : $('#activeOptions').val().replace('.','').split(';');
  551. if(extensions.length < 1){
  552. extensions = ['pdf'];
  553. if (typeof typeActive !== 'undefined') {
  554. if(typeActive == 12 | typeActive == 14){
  555. extensions = ['pdf', 'doc', 'docx', 'odt'];
  556. }
  557. }
  558. }
  559. $("#filepicker").fileinput({
  560. uploadUrl: "/procedure/files/"+$('input[name=procid].active-task').val(),
  561. uploadExtraData: {taskid:$('input[name=taskid].active-task').val()},
  562. uploadAsync: true,
  563. maxAjaxThreads: 1,
  564. showUpload: false,
  565. required: true,
  566. browseOnZoneClick: true,
  567. language: locale,
  568. allowedFileExtensions: extensions,
  569. previewFileIcon: '<i class="fas fa-file"></i>',
  570. theme: 'fas',
  571. allowedPreviewTypes: null,
  572. removeFromPreviewOnError: true,
  573. initialPreviewShowDelete: true,
  574. msgFileRequired: locale == 'es'?'Debe seleccionar la evidencia':'Ha de seleccionar l\'evidència',
  575. previewFileIconSettings: {
  576. 'pdf': '<i class="fas fa-file-pdf text-danger"></i>',
  577. },
  578. fileActionSettings: {
  579. showRemove: true,
  580. showZoom: false,
  581. showUpload: false,
  582. showDrag: false,
  583. removeIcon: '<i class="fas fa-trash"></i>'
  584. }
  585. });
  586. $('#filepicker').on('filebatchuploadcomplete', function(event, data, previewId, index) {
  587. location.reload();
  588. });
  589. tinymce.init({
  590. selector: '#editor',
  591. language: locale,
  592. browser_spellcheck: true,
  593. plugins: tinyplugins,
  594. menubar: tinymenu,
  595. toolbar: tinytoolbar,
  596. table_toolbar: tinytable,
  597. tinycomments_author: username,
  598. tinycomments_author_name: fullname,
  599. tinycomments_mode: 'embedded',
  600. tinycomments_can_delete: function(req, done, fail){ done({canDelete: true}); },
  601. table_advtab: tinyadvtab,
  602. table_row_advtab: tinyadvtab,
  603. table_cell_advtab: tinyadvtab,
  604. table_sizing_mode: 'fixed',
  605. table_default_styles: {
  606. width: '297mm'
  607. },
  608. content_style: "body { width: 297mm; max-width: 297mm; font-size: 10pt; font-family: Verdana, Geneva, sans-serif; font-style: normal; font-variant: normal;}",
  609. font_formats: "Arial=arial,helvetica,sans-serif; Verdana=verdana,geneva;",
  610. font_size_formats: '8pt 10pt 12pt 14pt 16pt 18pt 24pt',
  611. setup: tinyconfigure,
  612. save_onsavecallback: function(){
  613. if(autosaveTriggered){
  614. save_content(false);
  615. autosaveTriggered = false;
  616. }
  617. else{
  618. save_content(true);
  619. }
  620. },
  621. content_langs: [
  622. {title: 'Español', code: 'es'},
  623. {title: 'Valencià', code: 'ca'}],
  624. image_advtab: true,
  625. importcss_append: true,
  626. height: 700,
  627. image_caption: true,
  628. quickbars_selection_toolbar: '',
  629. toolbar_mode: 'sliding',
  630. contextmenu: tinycontextmenu,
  631. table_appearance_options: tinytableappearance,
  632. table_class_list: tinytableclass,
  633. table_resize_bars: tinytableresizebars,
  634. verify_html: false,
  635. cleanup: false,
  636. valid_elements: '+*[*]',
  637. paste_as_text: tinypasteplain,
  638. paste_remove_styles: tinypasteremovestyles,
  639. paste_strip_class_attributes: true
  640. });
  641. $('[data-toggle=confirmation]').confirmation({
  642. rootSelector: '[data-toggle=confirmation]',
  643. container: 'body',
  644. btnOkClass: 'btn btn-sm btn-success',
  645. btnOkLabel: 'Sí',
  646. btnCancelClass: 'btn btn-sm btn-danger',
  647. btnCancelLabel: 'No',
  648. });
  649. });
  650. $('#customFile').on("change", function() {
  651. var fileName = $(this).val().split("\\").pop();
  652. $(this).siblings(".custom-file-label").addClass("selected").html(fileName);
  653. if (fileName.split('.').pop().toLowerCase() == "pdf"){
  654. $('#custom-file-label').removeClass('file-invalid');
  655. }
  656. else{
  657. $('#custom-file-label').text(fileInvalid)
  658. $('#custom-file-label').addClass('file-invalid');
  659. $('#customFile').val("");
  660. }
  661. });
  662. function openProcedure(id){
  663. window.location.replace('/procedure/'+id);
  664. }
  665. function click_y(){
  666. $('#action_n').removeClass('btn-n-active');
  667. $('#action_y').addClass('btn-y-active');
  668. $('#response').val(1);
  669. }
  670. function click_n(){
  671. $('#action_y').removeClass('btn-y-active');
  672. $('#action_n').addClass('btn-n-active');
  673. $('#response').val(0);
  674. }
  675. function triggerSave(){
  676. autosaveTriggered = true;
  677. tinymce.activeEditor.execCommand('mceSave');
  678. console.log('Autosave triggered');
  679. }
  680. function keepalive(){
  681. $.get("/keepalive")
  682. .done(function(data){
  683. if(data != 'alive'){
  684. alert(expiredTxt.replaceAll("&#39;", "'"));
  685. saveWorker.terminate();
  686. aliveWorker.terminate();
  687. window.location.reload();
  688. }
  689. })
  690. .fail(function() {
  691. alert(expiredTxt.replaceAll("&#39;", "'"));
  692. saveWorker.terminate();
  693. aliveWorker.terminate();
  694. window.location.reload();
  695. });
  696. }
  697. function saveEditorWithConfirmation(){
  698. tinymce.activeEditor.setDirty(true);
  699. tinymce.activeEditor.execCommand('mceSave');
  700. alert(msgsave);
  701. }
  702. function createFilepicker(procId, taskId){
  703. $('.additional-file-container').empty();
  704. $('#additionalFileContainer'+taskId).html('<input class="additional-file-input" id="filepicker'+taskId+'" multiple type="file" name="evidencias">'
  705. +'<br><span class="btn btn-success pointer" onclick="$(\'#filepicker'+taskId+'\').fileinput(\'upload\');">Confirmar</span><br><br><br>');
  706. $("#filepicker"+taskId).fileinput({
  707. uploadUrl: "/procedure/files/"+procId,
  708. uploadExtraData: {taskid:taskId},
  709. uploadAsync: true,
  710. showUpload: true,
  711. required: true,
  712. browseOnZoneClick: true,
  713. language: locale,
  714. allowedFileExtensions: ['pdf'],
  715. previewFileIcon: '<i class="fas fa-file"></i>',
  716. theme: 'fas',
  717. allowedPreviewTypes: null,
  718. removeFromPreviewOnError: true,
  719. initialPreviewShowDelete: true,
  720. msgFileRequired: locale == 'es'?'Debe seleccionar la evidencia':'Ha de seleccionar l\'evidència',
  721. previewFileIconSettings: {
  722. 'pdf': '<i class="fas fa-file-pdf text-danger"></i>',
  723. },
  724. fileActionSettings: {
  725. showRemove: true,
  726. showZoom: false,
  727. showUpload: false,
  728. showDrag: false,
  729. removeIcon: '<i class="fas fa-trash"></i>'
  730. }
  731. });
  732. $('#filepicker'+taskId).on('filebatchuploadsuccess', function(event, data) {
  733. var form = data.form, files = data.files, extra = data.extra,
  734. response = data.response, reader = data.reader;
  735. window.location.href = window.location.href;
  736. });
  737. }
  738. function save_content(manualDraftSave){
  739. $.post("/procedure/save/"+idInstanciaTasca,
  740. {text:tinymce.activeEditor.getContent(),
  741. manual: manualDraftSave
  742. })
  743. .done(function(d){
  744. $('#lastmoddate').text(d);
  745. console.log('content saved at'+d);
  746. });
  747. }
  748. function print_to_pdf(content){
  749. $('<form id="tmpDownloadForm" method="post" action="/download/pdf/preview" target="_blank">' +
  750. '<input type="text" name="idtascai" value="'+$('input[name="taskid"]').val()+'" />' +
  751. '<textarea id="tmpDownloadContent" type="hidden" name="content">' +
  752. tinymce.activeEditor.getContent() +
  753. '</textarea>' +
  754. '</form>')
  755. .appendTo('body')
  756. .submit()
  757. .remove();
  758. }
  759. function showComments(){
  760. setTimeout(function(){
  761. if(typeActive == 15){
  762. tinymce.activeEditor.execCommand("ToggleSidebar", false, "showcomments");
  763. }
  764. }, 700);
  765. }
  766. function target_popup(form) {
  767. //window.open('', 'formpopup', 'width=1350,height=800,resizeable,scrollbars');
  768. window.open('', 'formpopup', 'width=1350,height=800,resizeable');
  769. form.target = 'formpopup';
  770. }
  771. function showFlowDiagram(){
  772. if($('#flowDiagramContainer').is(":visible")){
  773. hideFlowDiagram();
  774. }
  775. else{
  776. $('#flowDiagramContainer').show();
  777. mermaid.run({querySelector:'.mermaid'});
  778. }
  779. }
  780. function hideFlowDiagram(){
  781. $('#flowDiagramContainer').hide();
  782. }
  783. function downloadFlowDiagram() {
  784. const svgElement = document.querySelector('svg[aria-roledescription="flowchart-v2"]');
  785. const filename = 'flujo_procedimiento_'+$('#procedure_code').text();
  786. if(!svgElement) {
  787. console.error('No se encontró ningún SVG.');
  788. return;
  789. }
  790. const clonedSvg = svgElement.cloneNode(true);
  791. const width = parseInt(clonedSvg.getAttribute('width') || 800);
  792. const height = parseInt(clonedSvg.getAttribute('height') || 600);
  793. // Embebemos los estilos de Mermaid en el SVG
  794. const style = document.createElement('style');
  795. style.textContent = Array.from(document.styleSheets)
  796. .filter(s => !s.href || s.href.startsWith(window.location.origin))
  797. .map(sheet => {
  798. try {
  799. return Array.from(sheet.cssRules).map(rule => rule.cssText).join('\n');
  800. } catch (e) {
  801. return '';
  802. }
  803. })
  804. .join('\n');
  805. clonedSvg.insertBefore(style, clonedSvg.firstChild);
  806. const svgData = new XMLSerializer().serializeToString(clonedSvg);
  807. const svgBase64 = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svgData)));
  808. const img = new Image();
  809. img.onload = function () {
  810. const canvas = document.createElement('canvas');
  811. canvas.width = width;
  812. canvas.height = height;
  813. const ctx = canvas.getContext('2d');
  814. ctx.fillStyle = '#ffffff';
  815. ctx.fillRect(0, 0, canvas.width, canvas.height);
  816. ctx.drawImage(img, 0, 0);
  817. const pngUrl = canvas.toDataURL('image/png');
  818. const link = document.createElement('a');
  819. link.href = pngUrl;
  820. link.download = filename;
  821. document.body.appendChild(link);
  822. link.click();
  823. document.body.removeChild(link);
  824. };
  825. img.src = svgBase64;
  826. }
  827. </script>
  828. </body>
  829. </html>