Просмотр исходного кода

Añadida nueva gestion de responsables

Mario Martínez Hernández 2 месяцев назад
Родитель
Сommit
d431a188a8

+ 3 - 0
src/main/java/es/uv/saic/domain/UsuarisRol.java

@@ -5,6 +5,8 @@ import java.time.LocalDate;
 
 import jakarta.persistence.Column;
 import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
 import jakarta.persistence.Id;
 import jakarta.persistence.JoinColumn;
 import jakarta.persistence.JoinColumns;
@@ -23,6 +25,7 @@ public class UsuarisRol implements Serializable {
 	private static final long serialVersionUID = 1L;
 
 	@Id
+	@GeneratedValue(strategy = GenerationType.IDENTITY)
 	@Column(name="id_usuari_rol")
 	private Integer idUsuariRol;
 

+ 18 - 18
src/main/java/es/uv/saic/feign/AdminClient.java

@@ -18,40 +18,40 @@ import es.uv.saic.domain.AcreditacioTransfer;
 
 @FeignClient(name = "saic-core-service", contextId = "admin-controller")
 public interface AdminClient {
-    @PostMapping("/admins/instance")
+    @PostMapping("/admin/instance")
     HashMap<String, Object> instantiate(@RequestParam("procedure") Integer idProces,
 			@RequestParam("center") Integer idCentre,
 			@RequestParam("titulation") Integer idTitulacio);
      
-    @DeleteMapping("/admins/instance")
+    @DeleteMapping("/admin/instance")
     void deleteInstance(@RequestParam BigInteger idInstancia);
     
-    @PostMapping("/admins/instance/clear")
+    @PostMapping("/admin/instance/clear")
     String clearInstance(@RequestParam BigInteger idInstancia);
 
-    @PostMapping("/admins/instance/close")
+    @PostMapping("/admin/instance/close")
     String closeInstance(@RequestParam BigInteger idInstancia);
 
-    @DeleteMapping("/admins/instance/task")
+    @DeleteMapping("/admin/instance/task")
     String removeTask(@RequestParam BigInteger idInstanciaTasca);
 
     @PostMapping("/admin/instance/task/clear")
     String clearTask(@RequestParam BigInteger idInstanciaTasca);
 
-    @PostMapping("/admins/instance/task/reload")
+    @PostMapping("/admin/instance/task/reload")
     String reloadTask(@RequestParam BigInteger idInstanciaTasca);
 
-    @PostMapping("/admins/instance/task/reactivate")
+    @PostMapping("/admin/instance/task/reactivate")
     String activateTask(@RequestParam BigInteger idInstanciaTasca);
 
-    @PostMapping("/admins/instance/task/edit")
+    @PostMapping("/admin/instance/task/edit")
     String editTask(@RequestParam BigInteger idInstanciaTasca);
 
-    @PostMapping("/admins/mailing")
+    @PostMapping("/admin/mailing")
     void sendMails( @RequestParam Integer idRol, @RequestParam("centres[]") List<Integer> centres,
 			@RequestParam String subject, @RequestParam String body);
 
-    @PutMapping("/admins/procedures")
+    @PutMapping("/admin/procedures")
     void newProcedure(@RequestParam Map<String,String> params, 
 			@RequestParam(name="idTascap[]", required=false) List<String> idTascap, 
 			@RequestParam(name="dataLimit[]", required=false) List<String> dataLimit, 
@@ -70,7 +70,7 @@ public interface AdminClient {
 			@RequestParam(name="idPlantilla[]", required=false) List<String> idPlantilla,
 			@RequestParam(name="informe[]", required=false) List<Boolean> informe);
     
-    @PostMapping("/admins/procedures")
+    @PostMapping("/admin/procedures")
     void editProcedure(@RequestParam Map<String,String> params, 
 			@RequestParam(name="idTascap[]", required=false) List<String> idTascap, 
 			@RequestParam(name="dataLimit[]", required=false) List<String> dataLimit, 
@@ -89,21 +89,21 @@ public interface AdminClient {
 			@RequestParam(name="idPlantilla[]", required=false) List<String> idPlantilla,
 			@RequestParam(name="informe[]", required=false) List<Boolean> informe);
 
-    @DeleteMapping("/admins/procedures")
+    @DeleteMapping("/admin/procedures")
     void removeProcedure(@RequestParam("idProces") Integer idProces);    
     
-    @DeleteMapping("/admins/userrole")
+    @DeleteMapping("/admin/userrole")
     HashMap<String, Object> removeUserrole(@RequestParam("idRol") Integer idRol, @RequestParam("usuari") String usuari, 
 		@RequestParam("lugar") Integer lugar, @RequestParam("tlugar") String tlugar);
     
-    @PutMapping("/admins/userrole")
+    @PutMapping("/admin/userrole")
 	HashMap<String, Object> newUserrole(@RequestParam("idRol") Integer idRol, @RequestParam(name="usuari", required=false) String usuari, @RequestParam("centre") Integer idCentre, 
 			@RequestParam(name="titulacio", required=false) Integer idTitulacio, @RequestParam Map<String,String> params);
 	
-	@GetMapping("/admins/templates")
+	@GetMapping("/admin/templates")
 	List<String> getTemplates2();
 
-	@PostMapping("/admins/templates/inds/update")
+	@PostMapping("/admin/templates/inds/update")
 	void updateTemplateInds(@RequestParam(name="indicador[]", required=true) List<String> indicador,
 			@RequestParam(name="enquesta[]", required=true) List<String> enquesta,
 			@RequestParam(name="media[]", required=true) List<String> media,
@@ -111,10 +111,10 @@ public interface AdminClient {
 			@RequestParam(name="proces", required=true) String proces
 			);
 	
-	@GetMapping("/admins/acredita/{curs}/{grup}/{tambit}")  
+	@GetMapping("/admin/acredita/{curs}/{grup}/{tambit}")  
 	List<AcreditacioTransfer> acreditacionsByCurs(@PathVariable Integer curs, @PathVariable Integer grup, @PathVariable String tambit);
 
-	@PostMapping("/admins/acredita") 
+	@PostMapping("/admin/acredita") 
 	Acreditacio UpdateAcreditacio(@RequestParam String tlugar, @RequestParam Integer lugar, 
 									@RequestParam Integer grupCurs, @RequestParam Integer grupNum, @RequestParam Integer cursImpla,
 									@RequestParam String dataAcred, @RequestParam String dataRenov, @RequestParam String dataSegui,

+ 2 - 2
src/main/java/es/uv/saic/feign/EmailClient.java

@@ -8,9 +8,9 @@ import es.uv.saic.dto.EmailDTO;
 
 @FeignClient(name = "saic-core-service", contextId = "email-controller")
 public interface EmailClient {
-    @PostMapping("/emails/send")
+    @PostMapping("/email/send")
 	public void sendEmail(@RequestBody EmailDTO email);
 
-    @PostMapping("/emails/add")
+    @PostMapping("/email/add")
     public void addEmail(@RequestBody EmailDTO email);
 }

+ 5 - 2
src/main/java/es/uv/saic/feign/ManagerClient.java

@@ -10,9 +10,12 @@ import org.springframework.web.bind.annotation.RequestParam;
 
 @FeignClient(name = "saic-core-service", contextId = "manager-controller")
 public interface ManagerClient {
-    @PostMapping("/managers/form")
+    @PostMapping("/manager/form")
     HashMap<String, Object> managersForm(@RequestBody String usuari);
 
-    @PostMapping("/managers/search")
+    @PostMapping("/manager/search")
     HashMap<String, Object> managersSearch(@RequestParam("center") List<Integer> centres, @RequestParam(name="titulation[]", required=false) List<Integer> titulacions);
+
+    @PostMapping("/manager/list")
+    HashMap<String, Object> managers(@RequestParam(name="ambit", required=false) String ambit);
 }

+ 16 - 16
src/main/java/es/uv/saic/feign/OrganClient.java

@@ -17,53 +17,53 @@ import es.uv.saic.dto.OrganDTO;
 
 @FeignClient(name = "saic-core-service", contextId = "organ-controller")
 public interface OrganClient {
-    @GetMapping("/organs")
+    @GetMapping("/organ")
     List<OrganDTO> getCentres();
 
-    @GetMapping("/organs/active")
+    @GetMapping("/organ/active")
     List<OrganDTO> getActiveCentres();
 
-    @GetMapping("/organs/titulacions")
+    @GetMapping("/organ/titulacions")
     public List<OrganDTO> getTitulacionsWithCentre();
     
-    @GetMapping("/organs/indicadors/{ruct}/{curs}")
+    @GetMapping("/organ/indicadors/{ruct}/{curs}")
     public List<IndicadorEnquesta> getAllIndsByRuct(@PathVariable Integer ruct, @PathVariable Integer curs);
 
-    @GetMapping("/organs/find/{tlugar}/{idTitulacio}")
+    @GetMapping("/organ/find/{tlugar}/{idTitulacio}")
     public OrganDTO findByID(@PathVariable String tlugar,  @PathVariable Integer idTitulacio);
 
-    @GetMapping("/organs/supervisor")
+    @GetMapping("/organ/supervisor")
     public List<OrganDTO> getTitulacionsSupervisor();
      
-    @GetMapping("/organs/titulacions/{centre}")
+    @GetMapping("/organ/titulacions/{centre}")
     public List<OrganDTO> getTitulacionsByCentre(@PathVariable Integer centre);
 
-    @PostMapping("/organs/usuari")
+    @PostMapping("/organ/usuari")
     public List<OrganDTO> findOrgansByUsuari(@RequestBody Usuari usuari);
 
-    @GetMapping("/organs/findByRuct/{ruct}")
+    @GetMapping("/organ/findByRuct/{ruct}")
     public OrganDTO findByRuct(@PathVariable Integer ruct);
 
-    @PostMapping("/organs/titulacions/usuari")
+    @PostMapping("/organ/titulacions/usuari")
 	public HashMap<String, Object> getTitulationsByCenter(@RequestParam(name="centers[]", required=false) List<Integer> centres,
         @RequestParam String usuari ) throws IOException;
 
-    @PostMapping("/organs/titulacions/managers")
+    @PostMapping("/organ/titulacions/managers")
 	public HashMap<String, Object> getCenterTitulations(@RequestParam("center") Integer centre);
 
-    @PostMapping("/organs/titulacions/calendar")
+    @PostMapping("/organ/titulacions/calendar")
 	public HashMap<String, Object> getTitulationsByCenters(@RequestParam("centers[]") List<Integer> centres, @RequestParam("procedure") Integer idProces);
 
-    @PostMapping("/organs/titulacions/admin")
+    @PostMapping("/organ/titulacions/admin")
 	public HashMap<String, Object> getTitulationsByCenter(@RequestParam("center") Integer centre, @RequestParam("procedure") Integer idProces);
 
-    @PostMapping("/organs/titulacions")
+    @PostMapping("/organ/titulacions")
 	public HashMap<String, Object> getAllTitulationsByCenter(@RequestParam("center") Integer centre);
 
-    @PostMapping("/organs/centres")
+    @PostMapping("/organ/centres")
 	public HashMap<String, Object> getAllCentresByAmbit(@RequestParam("procedure") Integer idProces);
 
-    @PostMapping("/organs/equivalents")
+    @PostMapping("/organ/equivalents")
 	public List<Integer> getEquivalents(@RequestParam Integer lugar, @RequestParam String tlugar);
 
     @PostMapping("/organs/new/centre")

+ 4 - 5
src/main/java/es/uv/saic/web/AdminController.java

@@ -307,8 +307,8 @@ public class AdminController {
 				
 		session.setAttribute("respCentres", (List<Integer>) map.get("respCentres"));
 		session.setAttribute("respTitulacions", (List<Integer>) map.get("respTitulacions"));
-		
-		response.sendRedirect("/managers");
+
+		response.sendRedirect("/dashboard");
 	}
 	
 	// POST para añadir un nuevo usuario al sistema
@@ -323,9 +323,8 @@ public class AdminController {
 		session.setAttribute("respCentres", (List<Integer>) map.get("respCentres"));
 		session.setAttribute("respTitulacions", (List<Integer>) map.get("respTitulacions"));
 		session.setAttribute("roleExists", map.get("roleExists"));
-		
-		
-		response.sendRedirect("/managers");
+	
+	    response.sendRedirect("/dashboard");
 	}
 	
 	//¿POSIBLE ELIMINACIÓN?

+ 7 - 0
src/main/java/es/uv/saic/web/DashboardController.java

@@ -31,12 +31,16 @@ import es.uv.saic.dto.CategoriaDocumentDTO;
 import es.uv.saic.dto.InstanciaGanttDTOImp;
 import es.uv.saic.dto.OrganDTO;
 import es.uv.saic.feign.DashboardClient;
+import es.uv.saic.feign.ManagerClient;
 
 @Controller
 public class DashboardController {
 
 	@Autowired
 	private DashboardClient dbc;
+
+	@Autowired
+	private ManagerClient mc;
 	
 	@Autowired
     private ObjectMapper objectMapper;
@@ -101,10 +105,13 @@ public class DashboardController {
 
 			String status = response.get("redirect").toString();
 			if(status.equals("U")) {
+				model.addAllAttributes(mc.managers("U"));
 				return "dashboardUniversitat";
 			} else if (status.equals("C")) {
+				model.addAllAttributes(mc.managers("C"));
 				return "dashboardCentre";
 			} else if (status.equals("T")){
+				model.addAllAttributes(mc.managers("T"));
 				return "dashboardTitulacio";
 			}
         }

+ 1 - 1
src/main/java/es/uv/saic/web/ManagersController.java

@@ -63,7 +63,7 @@ public class ManagersController {
 	 * @Param titulacions List of titulacion ids to search managers in
 	 * @Return The view component list_managers
 	 */
-	@PostMapping("/managers/search")
+	@PostMapping("/manager/search")
 	public String managersSearch(Model model, Authentication auth, 
 			@RequestParam("center") List<Integer> centres,
 			@RequestParam(name="titulation[]", required=false) List<Integer> titulacions,

+ 7 - 0
src/main/resources/static/css/saic.css

@@ -595,6 +595,13 @@ header {
 /*** TABLES
 /*****************************************************************************************/
 
+/* Solo para Firefox */
+@supports (-moz-appearance: none) {
+  .tab-pane {
+    margin-top: 25px;
+  }
+}
+
 .table{
 	font-size:14px;
 	width:100%

+ 132 - 12
src/main/resources/templates/dashboardCentre.html

@@ -191,16 +191,16 @@
 														<span th:text="${item.acreditacio?.grupCurs+' - '+item.acreditacio?.grup}"></span>
 													</td>
 													<td>
-														<span th:text="${#dates.format(item.acreditacio?.dataAcred, 'dd/MM/yyyy')}"></span>
+														<span th:text="${#temporals.format(item.acreditacio?.dataAcred, 'dd/MM/yyyy')}"></span>
 													</td>
 													<td>
-														<span th:text="${#dates.format(item.acreditacio?.dataRenov, 'dd/MM/yyyy')}"></span>
+														<span th:text="${#temporals.format(item.acreditacio?.dataRenov, 'dd/MM/yyyy')}"></span>
 													</td>
 													<td>
-														<span th:text="${#dates.format(item.acreditacio?.dataSegui, 'dd/MM/yyyy')}"></span>
+														<span th:text="${#temporals.format(item.acreditacio?.dataSegui, 'dd/MM/yyyy')}"></span>
 													</td>
 													<td>
-														<span th:text="${#dates.format(item.acreditacio?.dataVerif, 'dd/MM/yyyy')}"></span>
+														<span th:text="${#temporals.format(item.acreditacio?.dataVerif, 'dd/MM/yyyy')}"></span>
 													</td>
 													<td>
 														<span th:text="${item.acreditacio?.cursImpla}"></span>
@@ -263,15 +263,44 @@
 	                            </div>
                             </div>
                             <div class="tab-pane" id="tab4">
-                                <div class="uv-table-group" th:if="${results}" style="cursor: auto;">
-									<div class="col-sm-12 uv-table-section" th:each="item : ${resp_titulacions}" style="margin-top:20px;">
-										<strong><span th:text="${#locale.language} == 'es' ? ${item.rol.descripcioCas}:${item.rol.descripcioVal}"></span>:</strong> 
-										<a th:href="'mailto:'+${item.usuari.email}" th:title="${item.usuari.email}" th:text="${item.usuari.nom + ' ' + item.usuari.cognoms}"></a> (<small><span th:text="#{managers.since}">Desde el</span> <span th:text="${item.inici}"></span></small>)
+								<span class="btn btn-primary pointer" id="btnAddManager" style="float:right; cursor:pointer; margin-right:20px;" data-toggle="modal" data-target="#newRoleModal" th:text="#{admin.managers.newRole}">Añadir responsable</span>
+								<div class="uv-table-group" th:if="${results}" style="cursor: auto; width: 90%;">
+
+									<div class="col-sm-12 uv-table-section" th:each="item : ${resp_titulacions}" style="display: flex; align-items: flex-start; margin-bottom: 15px;">
+    									<form enctype='multipart/form-data' method="POST" action="/admin/userrole/remove" style="margin-right: 15px;" onsubmit="var ok = confirm('¿Confirma que desea eliminar este responsable?');">
+											<input type="hidden" name="idRol" th:value="${item.rol.idRol}">
+											<input type="hidden" name="usuari" th:value="${item.usuari.usuari}">
+											<input type="hidden" name="tlugar" th:value="${item.organ.tlugar}">
+											<input type="hidden" name="lugar" th:value="${item.organ.lugar}">
+											<button class="btn" style="width:40px; height:38px; color: red;" th:title="#{admin.action.delete}">
+												<i class="fas fa-times"></i>
+											</button>
+										</form>
+
+										<div>
+											<strong th:text="${#locale.language} == 'es' ? ${item.rol.descripcioCas}:${item.rol.descripcioVal}"></strong>
+											<br>
+											<a th:href="'mailto:'+${item.usuari.email}" th:title="${item.usuari.email}" th:text="${item.usuari.nom + ' ' + item.usuari.cognoms}"></a> 
+											(<small><span th:text="#{managers.since}">Desde el</span> <span th:text="${item.inici}"></span></small>)
+										</div>
 									</div>
-									<div class="col-sm-12 uv-table-section" th:each="item : ${resp_centres}">
-										<strong th:text="${#locale.language} == 'es' ? ${item.rol.descripcioCas}:${item.rol.descripcioVal}"></strong>
-										<br>
-										<a th:href="'mailto:'+${item.usuari.email}" th:title="${item.usuari.email}" th:text="${item.usuari.nom + ' ' + item.usuari.cognoms}"></a> (<small><span th:text="#{managers.since}">Desde el</span> <span th:text="${item.inici}"></span></small>)
+									<div class="col-sm-12 uv-table-section" th:each="item : ${resp_centres}" style="display: flex; align-items: flex-start; margin-bottom: 15px;">
+    									<form enctype='multipart/form-data' method="POST" action="/admin/userrole/remove" style="margin-right: 15px;" onsubmit="return confirm('¿Confirma que desea eliminar este responsable?');">
+											<input type="hidden" name="idRol" th:value="${item.rol.idRol}">
+											<input type="hidden" name="usuari" th:value="${item.usuari.usuari}">
+											<input type="hidden" name="tlugar" th:value="${item.organ.tlugar}">
+											<input type="hidden" name="lugar" th:value="${item.organ.lugar}">
+											<button class="btn" style="width:40px; height:38px; color: red;" th:title="#{admin.action.delete}">
+												<i class="fas fa-times"></i>
+											</button>
+										</form>
+
+										<div>
+											<strong th:text="${#locale.language} == 'es' ? ${item.rol.descripcioCas}:${item.rol.descripcioVal}"></strong>
+											<br>
+											<a th:href="'mailto:'+${item.usuari.email}" th:title="${item.usuari.email}" th:text="${item.usuari.nom + ' ' + item.usuari.cognoms}"></a> 
+											(<small><span th:text="#{managers.since}">Desde el</span> <span th:text="${item.inici}"></span></small>)
+										</div>
 									</div>
 								</div>
                             </div>
@@ -383,6 +412,65 @@
 		</div>
 	</div>
 
+	<div class="modal fade" id="newRoleModal" tabindex="-1" role="dialog" aria-labelledby="newRoleModal" aria-hidden="true" th:if="${#authentication.principal.isAdmin() or #authentication.principal.isGranted()}">
+	  <div class="modal-dialog modal-lg">
+	    <div class="modal-content">
+		    <div class="modal-header">
+		        <h5 class="modal-title" th:text="#{admin.managers.newRole}">Añadir responsable</h5>
+		        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+		        	<span aria-hidden="true">&times;</span>
+		        </button>
+	        </div>
+	        <div class="modal-body">
+	        	<div class="container-fluid">
+			    	<form id="newRoleForm" enctype='multipart/form-data' method="POST" action="/admin/userrole/new" style="margin-top:25px;">
+						<input type="hidden" name="centre" th:value="${organ.lugar2}">
+						<input type="hidden" name="titulacio" th:value="${organ.lugar}">
+						<div class="form-group">
+							<label th:text="#{admin.managers.user}">Usuario</label>&nbsp;&nbsp;&nbsp;<span class="pointer" style="color:#007bff;" onclick="newUser();" th:title="#{admin.managers.newUser}"><i id="addUserIcon" class="fas fa-plus"></i></span>
+							<div id="userSelectorContainer">
+								<select class="selectpicker" id="userSelector" name="usuari" data-dropup-auto="false" data-live-search="true" data-width="100%" data-actions-box="true" required>
+							        <option th:each="item : ${users}" th:text="${item.nom}+' '+${item.cognoms}+' ('+${item.email}+')'" th:attr="value=${item.usuari}"></option>
+							    </select>
+						    </div>
+						    <div class="form-group" id="newUserContainer">
+						    	<div class="row">
+							    	<div class="form-group col-6">
+							    		<label  th:text="#{admin.managers.firstname}">Nombre</label>
+							    		<input class="form-control" type="text" value="" name="firstname" id="firstname">
+							    	</div>
+							    	<div class="form-group col-6">
+							    		<label th:text="#{admin.managers.lastname}">Apellidos</label>
+							    		<input class="form-control" type="text" value=""  name="lastname" id="lastname">
+							    	</div>
+							    	<div class="form-group col-6">
+							    		<label th:text="#{admin.managers.user}">Usuario</label>
+							    		<input class="form-control" type="text" value="" name="username" id="username" onkeyup="setEmail();">
+							    	</div>
+							    	<div class="form-group col-6">
+							    		<label  th:text="#{admin.managers.email}">Email</label>
+							    		<input class="form-control" type="email" value="" name="email" id="email"> 
+							    	</div>
+						    	</div>
+						    </div>
+						</div>
+						<div class="form-group">
+							<label th:text="#{admin.managers.role}">Rol</label>
+							<select class="selectpicker" id="roleSelector" name="idRol" data-dropup-auto="false" data-live-search="true" data-width="100%" data-actions-box="true" th:attr="data-none-selected-text=#{global.selectors.noData}" required>
+						        <option th:each="item : ${roles}" th:text="${#locale.language} == 'es' ? ${item.descripcioCas}:${item.descripcioVal}" th:attr="value=${item.idRol}"></option>
+						    </select>
+						</div>
+					</form>
+				</div>
+			</div>
+			<div class="modal-footer">
+		        <button type="button" class="btn btn-secondary" data-dismiss="modal" th:text="#{global.cancel}">Cancelar</button>
+		        <button class="btn btn-success" type="submit" form="newRoleForm" th:text="#{global.confirm}">Confirmar</button>
+	        </div>
+	    </div>
+	  </div>
+	</div>
+
 	<!-- contactModal -->
 	<div th:replace="~{layouts/common.html :: contactModal}"></div>
 	
@@ -469,6 +557,9 @@
 			});
 			
 			$('.data-selector-orig').trigger('change');
+
+			$('#newUserContainer').hide();
+			$('#userSelector').val('').trigger('change');
 			
 			treeDocuments = $('#treeDocuments').tree({
 				  cascadeSelection: false,
@@ -939,6 +1030,35 @@
 			$('#editCentreModal').modal('show');
 		}
 
+		function newUser(){
+			if(!$('#newUserContainer').is(":visible")){
+				$('#firstname').val('').attr('required', true);
+				$('#lastname').val('').attr('required', true);
+				$('#username').val('').attr('required', true);
+				$('#email').val('').attr('required', true);
+				$('#newUserContainer').show();
+				$('#userSelector').attr('required', false);
+				$('#userSelectorContainer').hide();
+				$('#addUserIcon').removeClass('fa-plus');
+				$('#addUserIcon').addClass('fa-minus');
+			}
+			else{
+				$('#firstname').val('').attr('required', false);
+				$('#lastname').val('').attr('required', false);
+				$('#username').val('').attr('required', false);
+				$('#email').val('').attr('required', false);
+				$('#newUserContainer').hide();
+				$('#userSelector').attr('required', true);
+				$('#userSelectorContainer').show();
+				$('#addUserIcon').removeClass('fa-minus');
+				$('#addUserIcon').addClass('fa-plus');
+			}	
+		}
+
+		function setEmail(){
+			$('#email').val($('#username').val()+'@uv.es');
+		}
+
 	</script>
 
 </body>

+ 125 - 8
src/main/resources/templates/dashboardTitulacio.html

@@ -287,15 +287,42 @@
 	                            </div>
                             </div>
                             <div class="tab-pane" id="tab4">
-                                <div class="uv-table-group" th:if="${results}" style="cursor: auto;">
-									<div class="col-sm-12 uv-table-section" th:each="item : ${resp_titulacions}" style="margin-top:20px;">
-										<strong><span th:text="${#locale.language} == 'es' ? ${item.rol.descripcioCas}:${item.rol.descripcioVal}"></span>: </strong> 
-										<a th:href="'mailto:'+${item.usuari.email}" th:title="${item.usuari.email}" th:text="${item.usuari.nom + ' ' + item.usuari.cognoms}"></a> (<small><span th:text="#{managers.since}">Desde el</span> <span th:text="${item.inici}"></span></small>)
+								<span class="btn btn-primary pointer" th:if="${#authentication.principal.isAdmin() or #authentication.principal.isGranted()}" id="btnAddManager" style="float:right; cursor:pointer; margin-right:20px;" data-toggle="modal" data-target="#newRoleModal" th:text="#{admin.managers.newRole}">Añadir responsable</span>
+                                <div class="uv-table-group" th:if="${results}" style="cursor: auto;  width: 90%">
+									<div class="col-sm-12 uv-table-section" th:each="item : ${resp_titulacions}" style="display: flex; align-items: flex-start; margin-bottom: 15px;">
+										<form enctype='multipart/form-data' method="POST" action="/admin/userrole/remove" style="margin-right: 15px;" th:if="${#authentication.principal.isAdmin() or #authentication.principal.isGranted()}" onsubmit="return confirm('¿Confirma que desea eliminar este responsable?');">
+											<input type="hidden" name="idRol" th:value="${item.rol.idRol}">
+											<input type="hidden" name="usuari" th:value="${item.usuari.usuari}">
+											<input type="hidden" name="tlugar" th:value="${item.organ.tlugar}">
+											<input type="hidden" name="lugar" th:value="${item.organ.lugar}">
+											<button class="btn" style="width:40px; height:38px; color: red;" th:title="#{admin.action.delete}">
+												<i class="fas fa-times"></i>
+											</button>
+										</form>
+										<div>
+											<strong th:text="${#locale.language} == 'es' ? ${item.rol.descripcioCas}:${item.rol.descripcioVal}"></strong>
+											<br>
+											<a th:href="'mailto:'+${item.usuari.email}" th:title="${item.usuari.email}" th:text="${item.usuari.nom + ' ' + item.usuari.cognoms}"></a> 
+											(<small><span th:text="#{managers.since}">Desde el</span> <span th:text="${item.inici}"></span></small>)
+										</div>									
 									</div>
-									<div class="col-sm-12 uv-table-section" th:each="item : ${resp_centres}">
-										<strong th:text="${#locale.language} == 'es' ? ${item.rol.descripcioCas}:${item.rol.descripcioVal}"></strong>
-										<br>
-										<a th:href="'mailto:'+${item.usuari.email}" th:title="${item.usuari.email}" th:text="${item.usuari.nom + ' ' + item.usuari.cognoms}"></a> (<small><span th:text="#{managers.since}">Desde el</span> <span th:text="${item.inici}"></span></small>)
+									<div class="col-sm-12 uv-table-section" th:each="item : ${resp_centres}" style="display: flex; align-items: flex-start; margin-bottom: 15px;">
+										<form enctype='multipart/form-data' method="POST" action="/admin/userrole/remove" style="margin-right: 15px;" th:if="${#authentication.principal.isAdmin() or #authentication.principal.isGranted()}" onsubmit="return confirm('¿Confirma que desea eliminar este responsable?');">
+											<input type="hidden" name="idRol" th:value="${item.rol.idRol}">
+											<input type="hidden" name="usuari" th:value="${item.usuari.usuari}">
+											<input type="hidden" name="tlugar" th:value="${item.organ.tlugar}">
+											<input type="hidden" name="lugar" th:value="${item.organ.lugar}">
+											<button class="btn" style="width:40px; height:38px; color: red;" th:title="#{admin.action.delete}">
+												<i class="fas fa-times"></i>
+											</button>
+										</form>
+										<div>
+										<div>
+											<strong th:text="${#locale.language} == 'es' ? ${item.rol.descripcioCas}:${item.rol.descripcioVal}"></strong>
+											<br>
+											<a th:href="'mailto:'+${item.usuari.email}" th:title="${item.usuari.email}" th:text="${item.usuari.nom + ' ' + item.usuari.cognoms}"></a> 
+											(<small><span th:text="#{managers.since}">Desde el</span> <span th:text="${item.inici}"></span></small>)
+										</div>
 									</div>
 								</div>
                             </div>
@@ -559,6 +586,64 @@
 		</div>
 	</div>
 
+	<div th:if="${#authentication.principal.isAdmin() or #authentication.principal.isGranted()}" class="modal fade" id="newRoleModal" tabindex="-1" role="dialog" aria-labelledby="newRoleModal" aria-hidden="true">
+	  <div class="modal-dialog modal-lg">
+	    <div class="modal-content">
+		    <div class="modal-header">
+		        <h5 class="modal-title" th:text="#{admin.managers.newRole}">Añadir responsable</h5>
+		        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+		        	<span aria-hidden="true">&times;</span>
+		        </button>
+	        </div>
+	        <div class="modal-body">
+	        	<div class="container-fluid">
+			    	<form id="newRoleForm" enctype='multipart/form-data' method="POST" action="/admin/userrole/new" style="margin-top:25px;">
+						<input type="hidden" name="centre" th:value="${organ.lugar2}">  
+			    		<input type="hidden" name="titulacio" th:value="${organ.lugar}">	
+						<div class="form-group">
+							<label th:text="#{admin.managers.user}">Usuario</label>&nbsp;&nbsp;&nbsp;<span class="pointer" style="color:#007bff;" onclick="newUser();" th:title="#{admin.managers.newUser}"><i id="addUserIcon" class="fas fa-plus"></i></span>
+							<div id="userSelectorContainer">
+								<select class="selectpicker" id="userSelector" name="usuari" data-dropup-auto="false" data-live-search="true" data-width="100%" data-actions-box="true" required>
+							        <option th:each="item : ${users}" th:text="${item.nom}+' '+${item.cognoms}+' ('+${item.email}+')'" th:attr="value=${item.usuari}"></option>
+							    </select>
+						    </div>
+						    <div class="form-group" id="newUserContainer">
+						    	<div class="row">
+							    	<div class="form-group col-6">
+							    		<label  th:text="#{admin.managers.firstname}">Nombre</label>
+							    		<input class="form-control" type="text" value="" name="firstname" id="firstname">
+							    	</div>
+							    	<div class="form-group col-6">
+							    		<label th:text="#{admin.managers.lastname}">Apellidos</label>
+							    		<input class="form-control" type="text" value=""  name="lastname" id="lastname">
+							    	</div>
+							    	<div class="form-group col-6">
+							    		<label th:text="#{admin.managers.user}">Usuario</label>
+							    		<input class="form-control" type="text" value="" name="username" id="username" onkeyup="setEmail();">
+							    	</div>
+							    	<div class="form-group col-6">
+							    		<label  th:text="#{admin.managers.email}">Email</label>
+							    		<input class="form-control" type="email" value="" name="email" id="email"> 
+							    	</div>
+						    	</div>
+						    </div>
+						</div>
+						<div class="form-group">
+							<label th:text="#{admin.managers.role}">Rol</label>
+							<select class="selectpicker" id="roleSelector" name="idRol" data-dropup-auto="false" data-live-search="true" data-width="100%" data-actions-box="true" th:attr="data-none-selected-text=#{global.selectors.noData}" required>
+						        <option th:each="item : ${roles}" th:text="${#locale.language} == 'es' ? ${item.descripcioCas}:${item.descripcioVal}" th:attr="value=${item.idRol}"></option>
+						    </select>
+						</div>
+					</form>
+				</div>
+			</div>
+			<div class="modal-footer">
+		        <button type="button" class="btn btn-secondary" data-dismiss="modal" th:text="#{global.cancel}">Cancelar</button>
+		        <button class="btn btn-success" type="submit" form="newRoleForm" th:text="#{global.confirm}">Confirmar</button>
+	        </div>
+	    </div>
+	</div>
+	
 	<!-- contactModal -->
 	<div th:replace="~{layouts/common.html :: contactModal}"></div>
 	
@@ -957,6 +1042,9 @@
 			    clearBtn: true
 			});
 			
+			$('#newUserContainer').hide();
+			$('#userSelector').val('').trigger('change');
+
 			treeProcedures = $('#treeProcedures').tree({
 				  cascadeSelection: false,
 				  cascadeCheck: false,
@@ -1267,6 +1355,35 @@
 			$('#editTitulacionModal').modal('show');
 		}
 
+		function newUser(){
+			if(!$('#newUserContainer').is(":visible")){
+				$('#firstname').val('').attr('required', true);
+				$('#lastname').val('').attr('required', true);
+				$('#username').val('').attr('required', true);
+				$('#email').val('').attr('required', true);
+				$('#newUserContainer').show();
+				$('#userSelector').attr('required', false);
+				$('#userSelectorContainer').hide();
+				$('#addUserIcon').removeClass('fa-plus');
+				$('#addUserIcon').addClass('fa-minus');
+			}
+			else{
+				$('#firstname').val('').attr('required', false);
+				$('#lastname').val('').attr('required', false);
+				$('#username').val('').attr('required', false);
+				$('#email').val('').attr('required', false);
+				$('#newUserContainer').hide();
+				$('#userSelector').attr('required', true);
+				$('#userSelectorContainer').show();
+				$('#addUserIcon').removeClass('fa-minus');
+				$('#addUserIcon').addClass('fa-plus');
+			}	
+		}
+
+		function setEmail(){
+			$('#email').val($('#username').val()+'@uv.es');
+		}
+
 	</script>
 
 </body>

+ 115 - 5
src/main/resources/templates/dashboardUniversitat.html

@@ -197,15 +197,33 @@
 	                            </div>
                             </div>
                             <div class="tab-pane" id="tab4">
-                                <div class="uv-table-group" th:if="${results}" style="cursor: auto;">
+								<span class="btn btn-primary pointer" id="btnAddManager" style="float:right; cursor:pointer; margin-right:20px;" data-toggle="modal" data-target="#newRoleModal" th:text="#{admin.managers.newRole}">Añadir responsable</span>
+								<div class="uv-table-group" th:if="${results}" style="cursor: auto; width: 90%;">
 									<div class="col-sm-12 uv-table-section" th:each="item : ${resp_titulacions}" style="margin-top:20px;">
+										<div class="col-sm-12 uv-table-section" th:each="item : ${resp_titulacions}" style="margin-top:20px;">
+											<strong><span th:text="${#locale.language} == 'es' ? ${item.rol.descripcioCas}:${item.rol.descripcioVal}"></span>: </strong> 
+											<a th:href="'mailto:'+${item.usuari.email}" th:title="${item.usuari.email}" th:text="${item.usuari.nom + ' ' + item.usuari.cognoms}"></a> (<small><span th:text="#{managers.since}">Desde el</span> <span th:text="${item.inici}"></span></small>)
+										</div>
 										<strong><span th:text="${#locale.language} == 'es' ? ${item.rol.descripcioCas}:${item.rol.descripcioVal}"></span>: </strong> 
 										<a th:href="'mailto:'+${item.usuari.email}" th:title="${item.usuari.email}" th:text="${item.usuari.nom + ' ' + item.usuari.cognoms}"></a> (<small><span th:text="#{managers.since}">Desde el</span> <span th:text="${item.inici}"></span></small>)
 									</div>
-									<div class="col-sm-12 uv-table-section" th:each="item : ${resp_centres}">
-										<strong th:text="${#locale.language} == 'es' ? ${item.rol.descripcioCas}:${item.rol.descripcioVal}"></strong>
-										<br>
-										<a th:href="'mailto:'+${item.usuari.email}" th:title="${item.usuari.email}" th:text="${item.usuari.nom + ' ' + item.usuari.cognoms}"></a> (<small><span th:text="#{managers.since}">Desde el</span> <span th:text="${item.inici}"></span></small>)
+									<div class="col-sm-12 uv-table-section" th:each="item : ${resp_centres}" style="display: flex; align-items: flex-start; margin-bottom: 15px;">
+    									<form enctype='multipart/form-data' method="POST" action="/admin/userrole/remove" style="margin-right: 15px;" onsubmit="return confirm('¿Confirma que desea eliminar este responsable?');">
+											<input type="hidden" name="idRol" th:value="${item.rol.idRol}">
+											<input type="hidden" name="usuari" th:value="${item.usuari.usuari}">
+											<input type="hidden" name="tlugar" th:value="${item.organ.tlugar}">
+											<input type="hidden" name="lugar" th:value="${item.organ.lugar}">
+											<button class="btn" style="width:40px; height:38px; color: red;" th:title="#{admin.action.delete}">
+												<i class="fas fa-times"></i>
+											</button>
+										</form>
+
+										<div>
+											<strong th:text="${#locale.language} == 'es' ? ${item.rol.descripcioCas}:${item.rol.descripcioVal}"></strong>
+											<br>
+											<a th:href="'mailto:'+${item.usuari.email}" th:title="${item.usuari.email}" th:text="${item.usuari.nom + ' ' + item.usuari.cognoms}"></a> 
+											(<small><span th:text="#{managers.since}">Desde el</span> <span th:text="${item.inici}"></span></small>)
+										</div>
 									</div>
 								</div>
                             </div>
@@ -366,6 +384,66 @@
 	  </div>
 	</div>
 
+	<div class="modal fade" id="newRoleModal" tabindex="-1" role="dialog" aria-labelledby="newRoleModal" aria-hidden="true" th:if="${#authentication.principal.isAdmin() or #authentication.principal.isGranted()}">
+	  <div class="modal-dialog modal-lg">
+	    <div class="modal-content">
+		    <div class="modal-header">
+		        <h5 class="modal-title" th:text="#{admin.managers.newRole}">Añadir responsable</h5>
+		        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+		        	<span aria-hidden="true">&times;</span>
+		        </button>
+	        </div>
+	        <div class="modal-body">
+	        	<div class="container-fluid">
+			    	<form id="newRoleForm" enctype='multipart/form-data' method="POST" action="/admin/userrole/new" style="margin-top:25px;">
+						<input type="hidden" name="centre" th:value="${organ.lugar2}">
+						<input type="hidden" name="titulacio" th:value="${organ.lugar}">  
+
+						<div class="form-group">
+							<label th:text="#{admin.managers.user}">Usuario</label>&nbsp;&nbsp;&nbsp;<span class="pointer" style="color:#007bff;" onclick="newUser();" th:title="#{admin.managers.newUser}"><i id="addUserIcon" class="fas fa-plus"></i></span>
+							<div id="userSelectorContainer">
+								<select class="selectpicker" id="userSelector" name="usuari" data-dropup-auto="false" data-live-search="true" data-width="100%" data-actions-box="true" required>
+							        <option th:each="item : ${users}" th:text="${item.nom}+' '+${item.cognoms}+' ('+${item.email}+')'" th:attr="value=${item.usuari}"></option>
+							    </select>
+						    </div>
+						    <div class="form-group" id="newUserContainer">
+						    	<div class="row">
+							    	<div class="form-group col-6">
+							    		<label  th:text="#{admin.managers.firstname}">Nombre</label>
+							    		<input class="form-control" type="text" value="" name="firstname" id="firstname">
+							    	</div>
+							    	<div class="form-group col-6">
+							    		<label th:text="#{admin.managers.lastname}">Apellidos</label>
+							    		<input class="form-control" type="text" value=""  name="lastname" id="lastname">
+							    	</div>
+							    	<div class="form-group col-6">
+							    		<label th:text="#{admin.managers.user}">Usuario</label>
+							    		<input class="form-control" type="text" value="" name="username" id="username" onkeyup="setEmail();">
+							    	</div>
+							    	<div class="form-group col-6">
+							    		<label  th:text="#{admin.managers.email}">Email</label>
+							    		<input class="form-control" type="email" value="" name="email" id="email"> 
+							    	</div>
+						    	</div>
+						    </div>
+						</div>
+						<div class="form-group">
+							<label th:text="#{admin.managers.role}">Rol</label>
+							<select class="selectpicker" id="roleSelector" name="idRol" data-dropup-auto="false" data-live-search="true" data-width="100%" data-actions-box="true" th:attr="data-none-selected-text=#{global.selectors.noData}" required>
+						        <option th:each="item : ${roles}" th:text="${#locale.language} == 'es' ? ${item.descripcioCas}:${item.descripcioVal}" th:attr="value=${item.idRol}"></option>
+						    </select>
+						</div>
+					</form>
+				</div>
+			</div>
+			<div class="modal-footer">
+		        <button type="button" class="btn btn-secondary" data-dismiss="modal" th:text="#{global.cancel}">Cancelar</button>
+		        <button class="btn btn-success" type="submit" form="newRoleForm" th:text="#{global.confirm}">Confirmar</button>
+	        </div>
+	    </div>
+	  </div>
+	</div>
+
 	<!-- contactModal -->
 	<div th:replace="~{layouts/common.html :: contactModal}"></div>
 	
@@ -453,6 +531,9 @@
 			});
 			
 			$('.data-selector-orig').trigger('change');
+
+			$('#newUserContainer').hide();
+			$('#userSelector').val('').trigger('change');
 			
 			treeDocuments = $('#treeDocuments').tree({
 				  cascadeSelection: false,
@@ -902,6 +983,35 @@
 			$('.selectpicker').selectpicker('refresh');
 		}
 
+		function newUser(){
+			if(!$('#newUserContainer').is(":visible")){
+				$('#firstname').val('').attr('required', true);
+				$('#lastname').val('').attr('required', true);
+				$('#username').val('').attr('required', true);
+				$('#email').val('').attr('required', true);
+				$('#newUserContainer').show();
+				$('#userSelector').attr('required', false);
+				$('#userSelectorContainer').hide();
+				$('#addUserIcon').removeClass('fa-plus');
+				$('#addUserIcon').addClass('fa-minus');
+			}
+			else{
+				$('#firstname').val('').attr('required', false);
+				$('#lastname').val('').attr('required', false);
+				$('#username').val('').attr('required', false);
+				$('#email').val('').attr('required', false);
+				$('#newUserContainer').hide();
+				$('#userSelector').attr('required', true);
+				$('#userSelectorContainer').show();
+				$('#addUserIcon').removeClass('fa-minus');
+				$('#addUserIcon').addClass('fa-plus');
+			}	
+		}
+
+		function setEmail(){
+			$('#email').val($('#username').val()+'@uv.es');
+		}
+
 	</script>
 
 </body>

+ 1 - 1
src/main/resources/templates/layouts/sidebar.html

@@ -27,7 +27,7 @@
 			<a href="/dashboard" class="uv-list-group-item list-group-item list-group-item-action bg-light" th:text="#{global.menu.tits}"></a>	 
 			<a href="/procedures" class="uv-list-group-item list-group-item list-group-item-action bg-light" th:text="#{global.menu.procedures}"></a>
 			<a href="/supervision" class="uv-list-group-item list-group-item list-group-item-action bg-light" th:text="#{global.menu.supervision}"></a>
-			<a href="/managers" class="uv-list-group-item list-group-item list-group-item-action bg-light" th:text="#{global.menu.managers}"></a>
+			<!--<a href="/managers" class="uv-list-group-item list-group-item list-group-item-action bg-light" th:text="#{global.menu.managers}"></a>-->
 		</div>
 		<br>
 		<div class="list-group list-group-flush" th:if="${#authentication.principal.isAdmin() or #authentication.principal.isDataTest()}">

+ 1 - 1
src/main/resources/templates/managers.html

@@ -214,7 +214,7 @@
 		}
 		
 		function search(){
-			$.post("/managers/search", {'center':$('#centersSelector').val(), 'titulation[]':$('#titulationsSelector').val()}, function(data) {
+			$.post("/manager/search", {'center':$('#centersSelector').val(), 'titulation[]':$('#titulationsSelector').val()}, function(data) {
 				$("#results")
 				   .html(data)
 				   .selectpicker('refresh');