integrated module confirmation
This commit is contained in:
		| @@ -57,7 +57,7 @@ public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter { | ||||
| 		super.configure(http); | ||||
| 		http.authorizeRequests() | ||||
| 		// @formatter:off | ||||
| 		  .antMatchers("/dashboard/**", "/business/**").authenticated() | ||||
| 		  .antMatchers("/dashboard/**", "/business/**", "/confirmation/**").authenticated() | ||||
|   		.anyRequest().permitAll(); | ||||
|   	// @formatter:on | ||||
| //		http.anonymous().disable(); | ||||
|   | ||||
| @@ -9,7 +9,7 @@ import org.springframework.web.bind.annotation.GetMapping; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author henkej | ||||
|  * @author jotty | ||||
|  * | ||||
|  */ | ||||
| @Controller | ||||
|   | ||||
| @@ -16,7 +16,7 @@ import de.jottyfan.camporganizer.module.business.privileges.impl.PrivilegesBean; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author henkej | ||||
|  * @author jotty | ||||
|  * | ||||
|  */ | ||||
| @Controller | ||||
|   | ||||
| @@ -0,0 +1,47 @@ | ||||
| package de.jottyfan.camporganizer.module.confirmation.confirmation; | ||||
|  | ||||
| import java.time.LocalDate; | ||||
| import java.util.List; | ||||
|  | ||||
| import javax.servlet.http.HttpServletRequest; | ||||
|  | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.stereotype.Controller; | ||||
| import org.springframework.ui.Model; | ||||
| import org.springframework.web.bind.annotation.GetMapping; | ||||
|  | ||||
| import de.jottyfan.camporganizer.module.confirmation.confirmation.impl.CampOverviewBean; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author jotty | ||||
|  * | ||||
|  */ | ||||
| @Controller | ||||
| public class ConfirmationController { | ||||
|  | ||||
| 	@Autowired | ||||
| 	private HttpServletRequest request; | ||||
|  | ||||
| 	@Autowired | ||||
| 	private IConfirmationService indexService; | ||||
|  | ||||
| 	@GetMapping("/confirmation") | ||||
| 	public String getIndex(Model model) { | ||||
| 		String username = indexService.getCurrentUser(request); | ||||
| 		List<CampOverviewBean> campoverview = indexService.getCampOverview(request); | ||||
| 		CampOverviewBean campoverviewsummary = new CampOverviewBean(LocalDate.now(), "summary"); | ||||
| 		for (CampOverviewBean bean : campoverview) { | ||||
| 			campoverviewsummary.setApproved(bean.getApproved() + campoverviewsummary.getApproved()); | ||||
| 			campoverviewsummary.setRejected(bean.getRejected() + campoverviewsummary.getRejected()); | ||||
| 			campoverviewsummary.setUntouched(bean.getUntouched() + campoverviewsummary.getUntouched()); | ||||
| 		} | ||||
| 		model.addAttribute("currentUser", username); | ||||
| 		model.addAttribute("campoverview", campoverview); | ||||
| 		model.addAttribute("campoverviewsummary", campoverviewsummary); | ||||
| 		model.addAttribute("untouched", indexService.getUntouched(request)); | ||||
| 		model.addAttribute("approved", indexService.getApproved(request)); | ||||
| 		model.addAttribute("rejected", indexService.getRejected(request)); | ||||
| 		return "confirmation/confirmation"; | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,55 @@ | ||||
| package de.jottyfan.camporganizer.module.confirmation.confirmation; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import javax.servlet.http.HttpServletRequest; | ||||
|  | ||||
| import de.jottyfan.camporganizer.module.confirmation.confirmation.impl.BookingBean; | ||||
| import de.jottyfan.camporganizer.module.confirmation.confirmation.impl.CampOverviewBean; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author jotty | ||||
|  * | ||||
|  */ | ||||
| public interface IConfirmationService { | ||||
| 	/** | ||||
| 	 * get the user of this session | ||||
| 	 * | ||||
| 	 * @param request the request | ||||
| 	 * @return the username of the current user | ||||
| 	 */ | ||||
| 	public String getCurrentUser(HttpServletRequest request); | ||||
|  | ||||
| 	/** | ||||
| 	 * get a list of the camps and its booking status | ||||
| 	 * | ||||
| 	 * @param request the request | ||||
| 	 * @return the camp overview beans | ||||
| 	 */ | ||||
| 	public List<CampOverviewBean> getCampOverview(HttpServletRequest request); | ||||
|  | ||||
| 	/** | ||||
| 	 * get a list of bookings that have not yet been worked on | ||||
| 	 * | ||||
| 	 * @param request the request | ||||
| 	 * @return the list of untouched bookings | ||||
| 	 */ | ||||
| 	public List<BookingBean> getUntouched(HttpServletRequest request); | ||||
|  | ||||
| 	/** | ||||
| 	 * get a list of approved bookings | ||||
| 	 * | ||||
| 	 * @param request the request | ||||
| 	 * @return the list of approved bookings | ||||
| 	 */ | ||||
| 	public List<BookingBean> getApproved(HttpServletRequest request); | ||||
|  | ||||
| 	/** | ||||
| 	 * get a list of rejected bookings | ||||
| 	 * | ||||
| 	 * @param request the request | ||||
| 	 * @return the list of rejected bookings | ||||
| 	 */ | ||||
| 	public List<BookingBean> getRejected(HttpServletRequest request); | ||||
| } | ||||
| @@ -0,0 +1,113 @@ | ||||
| package de.jottyfan.camporganizer.module.confirmation.confirmation.impl; | ||||
|  | ||||
| import java.io.Serializable; | ||||
| import java.time.LocalDate; | ||||
| import java.time.LocalDateTime; | ||||
|  | ||||
| import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author jotty | ||||
|  * | ||||
|  */ | ||||
| public class BookingBean implements Serializable, Comparable<BookingBean> { | ||||
| 	private static final long serialVersionUID = 2L; | ||||
|  | ||||
| 	private final Integer pkPerson; | ||||
| 	private final LocalDate date; | ||||
| 	private final String camp; | ||||
| 	private String fullname; | ||||
| 	private String role; | ||||
| 	private LocalDateTime registered; | ||||
|  | ||||
| 	public BookingBean(Integer pkPerson, LocalDate date, String camp) { | ||||
| 		this.pkPerson = pkPerson; | ||||
| 		this.date = date; | ||||
| 		this.camp = camp; | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public int compareTo(BookingBean bean) { | ||||
| 		return date == null || bean == null || bean.getDate() == null ? 0 : bean.getDate().compareTo(date); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * get the German role name | ||||
| 	 * | ||||
| 	 * @return the German role name | ||||
| 	 */ | ||||
| 	public String getRolename() { | ||||
| 		if (EnumCamprole.student.getLiteral().equals(role)) { | ||||
| 			return "Teilnehmer"; | ||||
| 		} else if (EnumCamprole.teacher.getLiteral().equals(role)) { | ||||
| 			return "Mitarbeiter"; | ||||
| 		} else if (EnumCamprole.director.getLiteral().equals(role)) { | ||||
| 			return "Leiter"; | ||||
| 		} else if (EnumCamprole.feeder.getLiteral().equals(role)) { | ||||
| 			return "Küchenteam"; | ||||
| 		} else { | ||||
| 			return role; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the fullname | ||||
| 	 */ | ||||
| 	public String getFullname() { | ||||
| 		return fullname; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param fullname the fullname to set | ||||
| 	 */ | ||||
| 	public void setFullname(String fullname) { | ||||
| 		this.fullname = fullname; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the role | ||||
| 	 */ | ||||
| 	public String getRole() { | ||||
| 		return role; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param role the role to set | ||||
| 	 */ | ||||
| 	public void setRole(String role) { | ||||
| 		this.role = role; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the registered | ||||
| 	 */ | ||||
| 	public LocalDateTime getRegistered() { | ||||
| 		return registered; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param registered the registered to set | ||||
| 	 */ | ||||
| 	public void setRegistered(LocalDateTime registered) { | ||||
| 		this.registered = registered; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the date | ||||
| 	 */ | ||||
| 	public LocalDate getDate() { | ||||
| 		return date; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the camp | ||||
| 	 */ | ||||
| 	public String getCamp() { | ||||
| 		return camp; | ||||
| 	} | ||||
|  | ||||
| 	public Integer getPkPerson() { | ||||
| 		return pkPerson; | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,88 @@ | ||||
| package de.jottyfan.camporganizer.module.confirmation.confirmation.impl; | ||||
|  | ||||
| import java.io.Serializable; | ||||
| import java.time.LocalDate; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author jotty | ||||
|  * | ||||
|  */ | ||||
| public class CampOverviewBean implements Serializable, Comparable<CampOverviewBean> { | ||||
| 	private static final long serialVersionUID = 1L; | ||||
|  | ||||
| 	private final LocalDate date; | ||||
| 	private final String camp; | ||||
| 	private Integer approved; | ||||
| 	private Integer rejected; | ||||
| 	private Integer untouched; | ||||
|  | ||||
| 	public CampOverviewBean(LocalDate date, String camp) { | ||||
| 		this.date = date; | ||||
| 		this.camp = camp; | ||||
| 		this.approved = 0; | ||||
| 		this.rejected = 0; | ||||
| 		this.untouched = 0; | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public int compareTo(CampOverviewBean bean) { | ||||
| 		return date == null || bean == null || bean.getDate() == null ? 0 : bean.getDate().compareTo(date); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the camp | ||||
| 	 */ | ||||
| 	public String getCamp() { | ||||
| 		return camp; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the approved | ||||
| 	 */ | ||||
| 	public Integer getApproved() { | ||||
| 		return approved; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param approved the approved to set | ||||
| 	 */ | ||||
| 	public void setApproved(Integer approved) { | ||||
| 		this.approved = approved; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the rejected | ||||
| 	 */ | ||||
| 	public Integer getRejected() { | ||||
| 		return rejected; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param rejected the rejected to set | ||||
| 	 */ | ||||
| 	public void setRejected(Integer rejected) { | ||||
| 		this.rejected = rejected; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the untouched | ||||
| 	 */ | ||||
| 	public Integer getUntouched() { | ||||
| 		return untouched; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param untouched the untouched to set | ||||
| 	 */ | ||||
| 	public void setUntouched(Integer untouched) { | ||||
| 		this.untouched = untouched; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the date | ||||
| 	 */ | ||||
| 	public LocalDate getDate() { | ||||
| 		return date; | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,162 @@ | ||||
| package de.jottyfan.camporganizer.module.confirmation.confirmation.impl; | ||||
|  | ||||
| import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMP; | ||||
| import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMPPROFILE; | ||||
| import static de.jottyfan.camporganizer.db.jooq.Tables.T_PERSON; | ||||
| import static de.jottyfan.camporganizer.db.jooq.Tables.T_PROFILE; | ||||
|  | ||||
| import java.time.LocalDateTime; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
|  | ||||
| import org.apache.logging.log4j.LogManager; | ||||
| import org.apache.logging.log4j.Logger; | ||||
| import org.jooq.Condition; | ||||
| import org.jooq.DSLContext; | ||||
| import org.jooq.Name; | ||||
| import org.jooq.Record4; | ||||
| import org.jooq.Record7; | ||||
| import org.jooq.SelectHavingStep; | ||||
| import org.jooq.SelectSeekStep1; | ||||
| import org.jooq.impl.DSL; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.stereotype.Repository; | ||||
| import org.springframework.transaction.annotation.Transactional; | ||||
|  | ||||
| import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole; | ||||
| import de.jottyfan.camporganizer.db.jooq.enums.EnumModule; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author jotty | ||||
|  * | ||||
|  */ | ||||
| @Repository | ||||
| @Transactional(transactionManager = "transactionManager") | ||||
| public class ConfirmationGateway { | ||||
| 	private static final Logger LOGGER = LogManager.getLogger(ConfirmationGateway.class); | ||||
|  | ||||
| 	@Autowired | ||||
| 	private DSLContext jooq; | ||||
|  | ||||
| 	/** | ||||
| 	 * get an overview of camps | ||||
| 	 * | ||||
| 	 * @param currentUser the current user to check for its privileges | ||||
| 	 * @return a list of camp overview beans | ||||
| 	 */ | ||||
| 	public List<CampOverviewBean> getCampOverviewBeans(String currentUser) { | ||||
| 		Name COUNT = DSL.name("count"); | ||||
| 		SelectHavingStep<Record4<Integer, Boolean, String, LocalDateTime>> sql = jooq | ||||
| 		// @formatter:off | ||||
| 			.select(DSL.count(T_PERSON.PK).as(COUNT), | ||||
| 					    T_PERSON.ACCEPT, | ||||
| 					    T_CAMP.NAME, | ||||
| 					    T_CAMP.ARRIVE) | ||||
| 			.from(T_PERSON) | ||||
| 			.leftJoin(T_CAMP).on(T_CAMP.PK.eq(T_PERSON.FK_CAMP)) | ||||
| 			.leftJoin(T_CAMPPROFILE).on(T_CAMPPROFILE.FK_CAMP.eq(T_PERSON.FK_CAMP)) | ||||
| 			.leftJoin(T_PROFILE).on(T_PROFILE.PK.eq(T_CAMPPROFILE.FK_PROFILE)) | ||||
| 			.where(T_CAMP.ARRIVE.isNotNull()) | ||||
| 			.and(T_CAMPPROFILE.MODULE.eq(EnumModule.registration)) | ||||
| 			.and(T_PROFILE.USERNAME.eq(currentUser)) | ||||
| 			.groupBy(T_CAMP.NAME, T_CAMP.ARRIVE, T_PERSON.ACCEPT); | ||||
| 		// @formatter:on | ||||
| 		LOGGER.debug(sql.toString()); | ||||
| 		Map<LocalDateTime, CampOverviewBean> map = new HashMap<>(); | ||||
| 		for (Record4<Integer, Boolean, String, LocalDateTime> r : sql.fetch()) { | ||||
| 			Integer count = r.get(COUNT, Integer.class); | ||||
| 			Boolean accept = r.get(T_PERSON.ACCEPT); | ||||
| 			String campname = r.get(T_CAMP.NAME); | ||||
| 			LocalDateTime arrive = r.get(T_CAMP.ARRIVE); | ||||
| 			CampOverviewBean bean = map.get(arrive); | ||||
| 			if (bean == null) { | ||||
| 				bean = new CampOverviewBean(arrive.toLocalDate(), campname); | ||||
| 				map.put(arrive, bean); | ||||
| 			} | ||||
| 			if (accept == null) { | ||||
| 				bean.setUntouched(count); | ||||
| 			} else if (accept) { | ||||
| 				bean.setApproved(count); | ||||
| 			} else { | ||||
| 				bean.setRejected(count); | ||||
| 			} | ||||
| 		} | ||||
| 		List<CampOverviewBean> list = new ArrayList<>(map.values()); | ||||
| 		Collections.sort(list); | ||||
| 		return list; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * get all bookings with condition that this user can manage | ||||
| 	 * | ||||
| 	 * @param currentUser the current user | ||||
| 	 * @param condition the condition e.g. T_PERSON.ACCEPT.isNull() | ||||
| 	 * @return a list of booking beans; an empty one at least | ||||
| 	 */ | ||||
| 	private List<BookingBean> getListWithCondition(String currentUser, Condition condition) { | ||||
| 		SelectSeekStep1<Record7<Integer, String, String, String, LocalDateTime, EnumCamprole, LocalDateTime>, LocalDateTime> sql = jooq | ||||
| 		// @formatter:off | ||||
| 			.select(T_PERSON.PK, T_PERSON.FORENAME, T_PERSON.SURNAME, T_CAMP.NAME, T_CAMP.ARRIVE, T_PERSON.CAMPROLE, T_PERSON.CREATED) | ||||
| 			.from(T_PERSON) | ||||
| 			.leftJoin(T_CAMP).on(T_CAMP.PK.eq(T_PERSON.FK_CAMP)) | ||||
| 			.leftJoin(T_CAMPPROFILE).on(T_CAMPPROFILE.FK_CAMP.eq(T_PERSON.FK_CAMP)) | ||||
| 			.leftJoin(T_PROFILE).on(T_PROFILE.PK.eq(T_CAMPPROFILE.FK_PROFILE)) | ||||
| 			.where(condition) | ||||
| 			.and(T_CAMPPROFILE.MODULE.eq(EnumModule.registration)) | ||||
| 			.and(T_PROFILE.USERNAME.eq(currentUser)) | ||||
| 			.orderBy(T_PERSON.CREATED.desc()); | ||||
| 		// @formatter:on | ||||
| 		LOGGER.debug(sql.toString()); | ||||
| 		List<BookingBean> list = new ArrayList<>(); | ||||
| 		for (Record7<Integer, String, String, String, LocalDateTime, EnumCamprole, LocalDateTime> r : sql.fetch()) { | ||||
| 			String forename = r.get(T_PERSON.FORENAME); | ||||
| 			String surname = r.get(T_PERSON.SURNAME); | ||||
| 			String campname = r.get(T_CAMP.NAME); | ||||
| 			LocalDateTime arrive = r.get(T_CAMP.ARRIVE); | ||||
| 			EnumCamprole role = r.get(T_PERSON.CAMPROLE); | ||||
| 			LocalDateTime created = r.get(T_PERSON.CREATED); | ||||
| 			Integer pkPerson = r.get(T_PERSON.PK); | ||||
| 			BookingBean bean = new BookingBean(pkPerson, arrive.toLocalDate(), campname); | ||||
| 			bean.setRole(role == null ? null : role.getLiteral()); | ||||
| 			bean.setRegistered(created); | ||||
| 			bean.setFullname(new StringBuilder().append(forename).append(" ").append(surname).toString()); | ||||
| 			list.add(bean); | ||||
| 		} | ||||
| 		return list; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * get all untouched bookings that this user can manage | ||||
| 	 * | ||||
| 	 * @param currentUser the current user | ||||
| 	 * @return a list of booking beans; an empty one at least | ||||
| 	 */ | ||||
| 	public List<BookingBean> getUntouched(String currentUser) { | ||||
| 		return getListWithCondition(currentUser, T_PERSON.ACCEPT.isNull()); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * get all approved bookings that this user can manage | ||||
| 	 * | ||||
| 	 * @param currentUser the current user | ||||
| 	 * @return a list of booking beans; an empty one at least | ||||
| 	 */ | ||||
| 	public List<BookingBean> getApproved(String currentUser) { | ||||
| 		return getListWithCondition(currentUser, T_PERSON.ACCEPT.isTrue()); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * get all rejected bookings that this user can manage | ||||
| 	 * | ||||
| 	 * @param currentUser the current user | ||||
| 	 * @return a list of booking beans; an empty one at least | ||||
| 	 */ | ||||
| 	public List<BookingBean> getRejected(String currentUser) { | ||||
| 		return getListWithCondition(currentUser, T_PERSON.ACCEPT.isFalse()); | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,53 @@ | ||||
| package de.jottyfan.camporganizer.module.confirmation.confirmation.impl; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import javax.servlet.http.HttpServletRequest; | ||||
|  | ||||
| import org.jooq.exception.DataAccessException; | ||||
| import org.keycloak.KeycloakSecurityContext; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.stereotype.Service; | ||||
|  | ||||
| import de.jottyfan.camporganizer.module.confirmation.confirmation.IConfirmationService; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author jotty | ||||
|  * | ||||
|  */ | ||||
| @Service | ||||
| public class ConfirmationService implements IConfirmationService { | ||||
| 	@Autowired | ||||
| 	private ConfirmationGateway gateway; | ||||
|  | ||||
| 	@Override | ||||
| 	public String getCurrentUser(HttpServletRequest request) { | ||||
| 		KeycloakSecurityContext ksc = (KeycloakSecurityContext) request | ||||
| 				.getAttribute(KeycloakSecurityContext.class.getName()); | ||||
| 		if (ksc == null) { | ||||
| 			throw new DataAccessException("no keycloak user in session"); | ||||
| 		} | ||||
| 		return ksc.getIdToken().getPreferredUsername(); | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public List<CampOverviewBean> getCampOverview(HttpServletRequest request) { | ||||
| 		return gateway.getCampOverviewBeans(getCurrentUser(request)); | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public List<BookingBean> getUntouched(HttpServletRequest request) { | ||||
| 		return gateway.getUntouched(getCurrentUser(request)); | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public List<BookingBean> getApproved(HttpServletRequest request) { | ||||
| 		return gateway.getApproved(getCurrentUser(request)); | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public List<BookingBean> getRejected(HttpServletRequest request) { | ||||
| 		return gateway.getRejected(getCurrentUser(request)); | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,47 @@ | ||||
| package de.jottyfan.camporganizer.module.confirmation.person; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import de.jottyfan.camporganizer.module.confirmation.person.impl.CampBean; | ||||
| import de.jottyfan.camporganizer.module.confirmation.person.impl.PersonBean; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author jotty | ||||
|  * | ||||
|  */ | ||||
| public interface IPersonService { | ||||
|  | ||||
| 	/** | ||||
| 	 * get person bean from db if username can read it | ||||
| 	 * | ||||
| 	 * @param username the user to read from the database | ||||
| 	 * @param pk       the ID of the person to be read | ||||
| 	 * @return the person or null | ||||
| 	 */ | ||||
| 	public PersonBean getPerson(String username, Integer pk); | ||||
|  | ||||
| 	/** | ||||
| 	 * update bean in the database | ||||
| 	 * | ||||
| 	 * @param bean the bean | ||||
| 	 * @return number of affected database rows | ||||
| 	 */ | ||||
| 	public Integer updatePerson(PersonBean bean); | ||||
|  | ||||
| 	/** | ||||
| 	 * get all camps from the database that the user has access to | ||||
| 	 * | ||||
| 	 * @param username the user | ||||
| 	 * @return a list of camp beans; an empty one at least | ||||
| 	 */ | ||||
| 	public List<CampBean> getCamps(String username); | ||||
|  | ||||
| 	/** | ||||
| 	 * get several annotations as a String | ||||
| 	 * | ||||
| 	 * @param pk the ID of the person | ||||
| 	 * @return the annotations; may be an empty string | ||||
| 	 */ | ||||
| 	public String getAnnotations(Integer pk); | ||||
| } | ||||
| @@ -0,0 +1,48 @@ | ||||
| package de.jottyfan.camporganizer.module.confirmation.person; | ||||
|  | ||||
| import javax.servlet.http.HttpServletRequest; | ||||
|  | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.stereotype.Controller; | ||||
| import org.springframework.ui.Model; | ||||
| import org.springframework.web.bind.annotation.GetMapping; | ||||
| import org.springframework.web.bind.annotation.ModelAttribute; | ||||
| import org.springframework.web.bind.annotation.PathVariable; | ||||
| import org.springframework.web.bind.annotation.PostMapping; | ||||
|  | ||||
| import de.jottyfan.camporganizer.module.confirmation.confirmation.IConfirmationService; | ||||
| import de.jottyfan.camporganizer.module.confirmation.person.impl.PersonBean; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author jotty | ||||
|  * | ||||
|  */ | ||||
| @Controller | ||||
| public class PersonController { | ||||
|  | ||||
| 	@Autowired | ||||
| 	private HttpServletRequest request; | ||||
|  | ||||
| 	@Autowired | ||||
| 	private IConfirmationService confirmationService; | ||||
|  | ||||
| 	@Autowired | ||||
| 	private IPersonService personService; | ||||
|  | ||||
| 	@GetMapping("/confirmation/person/{pk}") | ||||
| 	public String getIndex(Model model, @PathVariable Integer pk) { | ||||
| 		String username = confirmationService.getCurrentUser(request); | ||||
| 		model.addAttribute("currentUser", username); | ||||
| 		model.addAttribute("person", personService.getPerson(username, pk)); | ||||
| 		model.addAttribute("camps", personService.getCamps(username)); | ||||
| 		model.addAttribute("annotations", personService.getAnnotations(pk)); | ||||
| 		return "confirmation/person"; | ||||
| 	} | ||||
|  | ||||
| 	@PostMapping("/confirmation/person/update") | ||||
| 	public String doUpdate(@ModelAttribute PersonBean bean, Model model) { | ||||
| 		personService.updatePerson(bean); | ||||
| 		return "redirect:/confirmation"; | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,75 @@ | ||||
| package de.jottyfan.camporganizer.module.confirmation.person.impl; | ||||
|  | ||||
| import java.io.Serializable; | ||||
| import java.time.LocalDate; | ||||
| import java.time.LocalDateTime; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author jotty | ||||
|  * | ||||
|  */ | ||||
| public class CampBean implements Serializable { | ||||
| 	private static final long serialVersionUID = 1L; | ||||
|  | ||||
| 	private Integer pk; | ||||
| 	private String name; | ||||
| 	private LocalDateTime arrive; | ||||
| 	private String location; | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the pk | ||||
| 	 */ | ||||
| 	public Integer getPk() { | ||||
| 		return pk; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param pk the pk to set | ||||
| 	 */ | ||||
| 	public void setPk(Integer pk) { | ||||
| 		this.pk = pk; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the name | ||||
| 	 */ | ||||
| 	public String getName() { | ||||
| 		return name; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param name the name to set | ||||
| 	 */ | ||||
| 	public void setName(String name) { | ||||
| 		this.name = name; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the arrive | ||||
| 	 */ | ||||
| 	public LocalDateTime getArrive() { | ||||
| 		return arrive; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param arrive the arrive to set | ||||
| 	 */ | ||||
| 	public void setArrive(LocalDateTime arrive) { | ||||
| 		this.arrive = arrive; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the location | ||||
| 	 */ | ||||
| 	public String getLocation() { | ||||
| 		return location; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param location the location to set | ||||
| 	 */ | ||||
| 	public void setLocation(String location) { | ||||
| 		this.location = location; | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,276 @@ | ||||
| package de.jottyfan.camporganizer.module.confirmation.person.impl; | ||||
|  | ||||
| import java.io.Serializable; | ||||
| import java.time.LocalDate; | ||||
| import java.time.LocalDateTime; | ||||
|  | ||||
| import org.springframework.format.annotation.DateTimeFormat; | ||||
|  | ||||
| import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole; | ||||
| import de.jottyfan.camporganizer.db.jooq.enums.EnumSex; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author jotty | ||||
|  * | ||||
|  */ | ||||
| public class PersonBean implements Serializable { | ||||
| 	private static final long serialVersionUID = 2L; | ||||
|  | ||||
| 	private Integer pk; | ||||
| 	private String forename; | ||||
| 	private String surname; | ||||
| 	private String street; | ||||
| 	private String zip; | ||||
| 	private String city; | ||||
| 	private String phone; | ||||
| 	@DateTimeFormat(pattern = "yyyy-MM-dd") | ||||
| 	private LocalDate birthdate; | ||||
| 	private EnumCamprole camprole; | ||||
| 	private String email; | ||||
| 	private Integer fkCamp; | ||||
| 	private Integer fkProfile; | ||||
| 	private Boolean accept; | ||||
| 	private LocalDateTime created; | ||||
| 	private EnumSex sex; | ||||
| 	private Integer fkRegistrator; | ||||
| 	private String comment; | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the forename | ||||
| 	 */ | ||||
| 	public String getForename() { | ||||
| 		return forename; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param forename the forename to set | ||||
| 	 */ | ||||
| 	public void setForename(String forename) { | ||||
| 		this.forename = forename; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the surname | ||||
| 	 */ | ||||
| 	public String getSurname() { | ||||
| 		return surname; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param surname the surname to set | ||||
| 	 */ | ||||
| 	public void setSurname(String surname) { | ||||
| 		this.surname = surname; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the street | ||||
| 	 */ | ||||
| 	public String getStreet() { | ||||
| 		return street; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param street the street to set | ||||
| 	 */ | ||||
| 	public void setStreet(String street) { | ||||
| 		this.street = street; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the zip | ||||
| 	 */ | ||||
| 	public String getZip() { | ||||
| 		return zip; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param zip the zip to set | ||||
| 	 */ | ||||
| 	public void setZip(String zip) { | ||||
| 		this.zip = zip; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the city | ||||
| 	 */ | ||||
| 	public String getCity() { | ||||
| 		return city; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param city the city to set | ||||
| 	 */ | ||||
| 	public void setCity(String city) { | ||||
| 		this.city = city; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the phone | ||||
| 	 */ | ||||
| 	public String getPhone() { | ||||
| 		return phone; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param phone the phone to set | ||||
| 	 */ | ||||
| 	public void setPhone(String phone) { | ||||
| 		this.phone = phone; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the birthdate | ||||
| 	 */ | ||||
| 	public LocalDate getBirthdate() { | ||||
| 		return birthdate; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param birthdate the birthdate to set | ||||
| 	 */ | ||||
| 	public void setBirthdate(LocalDate birthdate) { | ||||
| 		this.birthdate = birthdate; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the camprole | ||||
| 	 */ | ||||
| 	public EnumCamprole getCamprole() { | ||||
| 		return camprole; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param camprole the camprole to set | ||||
| 	 */ | ||||
| 	public void setCamprole(EnumCamprole camprole) { | ||||
| 		this.camprole = camprole; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the email | ||||
| 	 */ | ||||
| 	public String getEmail() { | ||||
| 		return email; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param email the email to set | ||||
| 	 */ | ||||
| 	public void setEmail(String email) { | ||||
| 		this.email = email; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the fkCamp | ||||
| 	 */ | ||||
| 	public Integer getFkCamp() { | ||||
| 		return fkCamp; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param fkCamp the fkCamp to set | ||||
| 	 */ | ||||
| 	public void setFkCamp(Integer fkCamp) { | ||||
| 		this.fkCamp = fkCamp; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the fkProfile | ||||
| 	 */ | ||||
| 	public Integer getFkProfile() { | ||||
| 		return fkProfile; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param fkProfile the fkProfile to set | ||||
| 	 */ | ||||
| 	public void setFkProfile(Integer fkProfile) { | ||||
| 		this.fkProfile = fkProfile; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the accept | ||||
| 	 */ | ||||
| 	public Boolean getAccept() { | ||||
| 		return accept; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param accept the accept to set | ||||
| 	 */ | ||||
| 	public void setAccept(Boolean accept) { | ||||
| 		this.accept = accept; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the created | ||||
| 	 */ | ||||
| 	public LocalDateTime getCreated() { | ||||
| 		return created; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param created the created to set | ||||
| 	 */ | ||||
| 	public void setCreated(LocalDateTime created) { | ||||
| 		this.created = created; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the sex | ||||
| 	 */ | ||||
| 	public EnumSex getSex() { | ||||
| 		return sex; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param sex the sex to set | ||||
| 	 */ | ||||
| 	public void setSex(EnumSex sex) { | ||||
| 		this.sex = sex; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the fkRegistrator | ||||
| 	 */ | ||||
| 	public Integer getFkRegistrator() { | ||||
| 		return fkRegistrator; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param fkRegistrator the fkRegistrator to set | ||||
| 	 */ | ||||
| 	public void setFkRegistrator(Integer fkRegistrator) { | ||||
| 		this.fkRegistrator = fkRegistrator; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the comment | ||||
| 	 */ | ||||
| 	public String getComment() { | ||||
| 		return comment; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param comment the comment to set | ||||
| 	 */ | ||||
| 	public void setComment(String comment) { | ||||
| 		this.comment = comment; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the pk | ||||
| 	 */ | ||||
| 	public Integer getPk() { | ||||
| 		return pk; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param pk the pk to set | ||||
| 	 */ | ||||
| 	public void setPk(Integer pk) { | ||||
| 		this.pk = pk; | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,205 @@ | ||||
| package de.jottyfan.camporganizer.module.confirmation.person.impl; | ||||
|  | ||||
| import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMP; | ||||
| import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMPPROFILE; | ||||
| import static de.jottyfan.camporganizer.db.jooq.Tables.T_LOCATION; | ||||
| import static de.jottyfan.camporganizer.db.jooq.Tables.T_PERSON; | ||||
| import static de.jottyfan.camporganizer.db.jooq.Tables.T_PROFILE; | ||||
|  | ||||
| import java.time.LocalDate; | ||||
| import java.time.LocalDateTime; | ||||
| import java.time.format.DateTimeFormatter; | ||||
| import java.time.temporal.ChronoUnit; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| import org.apache.logging.log4j.LogManager; | ||||
| import org.apache.logging.log4j.Logger; | ||||
| import org.jooq.DSLContext; | ||||
| import org.jooq.Record; | ||||
| import org.jooq.Record11; | ||||
| import org.jooq.Record4; | ||||
| import org.jooq.SelectConditionStep; | ||||
| import org.jooq.SelectSeekStep1; | ||||
| import org.jooq.UpdateConditionStep; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.stereotype.Repository; | ||||
| import org.springframework.transaction.annotation.Transactional; | ||||
|  | ||||
| import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole; | ||||
| import de.jottyfan.camporganizer.db.jooq.enums.EnumModule; | ||||
| import de.jottyfan.camporganizer.db.jooq.tables.TProfile; | ||||
| import de.jottyfan.camporganizer.db.jooq.tables.records.TPersonRecord; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author jotty | ||||
|  * | ||||
|  */ | ||||
| @Repository | ||||
| @Transactional(transactionManager = "transactionManager") | ||||
| public class PersonGateway { | ||||
| 	private static final Logger LOGGER = LogManager.getLogger(PersonGateway.class); | ||||
|  | ||||
| 	@Autowired | ||||
| 	private DSLContext jooq; | ||||
|  | ||||
| 	/** | ||||
| 	 * get all camps from the database if username is allowed to maintain it | ||||
| 	 * | ||||
| 	 * @param username the username of the requesting person | ||||
| 	 * @return all camps | ||||
| 	 */ | ||||
| 	public List<CampBean> getCamps(String username) { | ||||
| 		SelectSeekStep1<Record4<Integer, String, LocalDateTime, String>, LocalDateTime> sql = jooq | ||||
| 		// @formatter:off | ||||
| 			.select(T_CAMP.PK, T_CAMP.NAME, T_CAMP.ARRIVE, T_LOCATION.NAME) | ||||
| 			.from(T_CAMP) | ||||
| 			.leftJoin(T_LOCATION).on(T_LOCATION.PK.eq(T_CAMP.FK_LOCATION)) | ||||
| 			.leftJoin(T_CAMPPROFILE).on(T_CAMPPROFILE.FK_CAMP.eq(T_CAMP.PK)) | ||||
| 			.leftJoin(T_PROFILE).on(T_PROFILE.PK.eq(T_CAMPPROFILE.FK_PROFILE)) | ||||
| 			.where(T_CAMPPROFILE.MODULE.eq(EnumModule.registration)) | ||||
| 			.and(T_PROFILE.USERNAME.eq(username)) | ||||
| 			.orderBy(T_CAMP.ARRIVE.desc()); | ||||
| 		// @formatter:on | ||||
| 		LOGGER.debug(sql.toString()); | ||||
| 		List<CampBean> list = new ArrayList<>(); | ||||
| 		for (Record r : sql.fetch()) { | ||||
| 			CampBean bean = new CampBean(); | ||||
| 			bean.setPk(r.get(T_CAMP.PK)); | ||||
| 			bean.setName(r.get(T_CAMP.NAME)); | ||||
| 			bean.setArrive(r.get(T_CAMP.ARRIVE)); | ||||
| 			bean.setLocation(r.get(T_LOCATION.NAME)); | ||||
| 			list.add(bean); | ||||
| 		} | ||||
| 		return list; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * get the person referenced by pk if username is allowed to read it | ||||
| 	 * | ||||
| 	 * @param username the username of the requesting person | ||||
| 	 * @param pk       the ID of the person | ||||
| 	 * @return the person bean if found; null otherwise | ||||
| 	 */ | ||||
| 	public PersonBean getPerson(String username, Integer pk) { | ||||
| 		SelectConditionStep<Record> sql = jooq | ||||
| 		// @formatter:off | ||||
| 			.select(T_PERSON.fields()) | ||||
| 			.from(T_PERSON) | ||||
| 			.leftJoin(T_CAMP).on(T_CAMP.PK.eq(T_PERSON.FK_CAMP)) | ||||
| 			.leftJoin(T_CAMPPROFILE).on(T_CAMPPROFILE.FK_CAMP.eq(T_PERSON.FK_CAMP)) | ||||
| 			.leftJoin(T_PROFILE).on(T_PROFILE.PK.eq(T_CAMPPROFILE.FK_PROFILE)) | ||||
| 			.where(T_PERSON.PK.eq(pk)) | ||||
| 			.and(T_CAMPPROFILE.MODULE.eq(EnumModule.registration)) | ||||
| 			.and(T_PROFILE.USERNAME.eq(username)); | ||||
| 		// @formatter:on | ||||
| 		LOGGER.debug(sql.toString()); | ||||
| 		for (Record r : sql.fetch()) { | ||||
| 			PersonBean bean = new PersonBean(); | ||||
| 			bean.setPk(pk); | ||||
| 			bean.setAccept(r.get(T_PERSON.ACCEPT)); | ||||
| 			bean.setBirthdate(r.get(T_PERSON.BIRTHDATE)); | ||||
| 			bean.setCamprole(r.get(T_PERSON.CAMPROLE)); | ||||
| 			bean.setCity(r.get(T_PERSON.CITY)); | ||||
| 			bean.setComment(r.get(T_PERSON.COMMENT)); | ||||
| 			bean.setCreated(r.get(T_PERSON.CREATED)); | ||||
| 			bean.setEmail(r.get(T_PERSON.EMAIL)); | ||||
| 			bean.setFkCamp(r.get(T_PERSON.FK_CAMP)); | ||||
| 			bean.setFkProfile(r.get(T_PERSON.FK_PROFILE)); | ||||
| 			bean.setFkRegistrator(r.get(T_PERSON.FK_REGISTRATOR)); | ||||
| 			bean.setForename(r.get(T_PERSON.FORENAME)); | ||||
| 			bean.setSurname(r.get(T_PERSON.SURNAME)); | ||||
| 			bean.setPhone(r.get(T_PERSON.PHONE)); | ||||
| 			bean.setSex(r.get(T_PERSON.SEX)); | ||||
| 			bean.setStreet(r.get(T_PERSON.STREET)); | ||||
| 			bean.setZip(r.get(T_PERSON.ZIP)); | ||||
| 			return bean; | ||||
| 		} | ||||
| 		return null; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * update that bean in the database | ||||
| 	 * | ||||
| 	 * @param bean the bean | ||||
| 	 * @return the number of affected database rows | ||||
| 	 */ | ||||
| 	public Integer updatePerson(PersonBean bean) { | ||||
| 		UpdateConditionStep<TPersonRecord> sql = jooq | ||||
| 		// @formatter:off | ||||
| 			.update(T_PERSON) | ||||
| 			.set(T_PERSON.FORENAME, bean.getForename()) | ||||
| 			.set(T_PERSON.SURNAME, bean.getSurname()) | ||||
| 			.set(T_PERSON.STREET, bean.getStreet()) | ||||
| 			.set(T_PERSON.ZIP, bean.getZip()) | ||||
| 			.set(T_PERSON.CITY, bean.getCity()) | ||||
| 			.set(T_PERSON.BIRTHDATE, bean.getBirthdate()) | ||||
| 			.set(T_PERSON.SEX, bean.getSex()) | ||||
| 			.set(T_PERSON.PHONE, bean.getPhone()) | ||||
| 			.set(T_PERSON.EMAIL, bean.getEmail()) | ||||
| 			.set(T_PERSON.COMMENT, bean.getComment()) | ||||
| 			.set(T_PERSON.ACCEPT, bean.getAccept()) | ||||
| 			.where(T_PERSON.PK.eq(bean.getPk())); | ||||
| 		// @formatter:on | ||||
| 		LOGGER.debug(sql.toString()); | ||||
| 		return sql.execute(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * get annotations of person | ||||
| 	 * | ||||
| 	 * @param pk the ID of the person | ||||
| 	 * @return annotations if found; an empty string if not | ||||
| 	 */ | ||||
| 	public String getAnnotations(Integer pk) { | ||||
| 		TProfile REGISTRATOR = T_PROFILE.as("registrator"); | ||||
| 		SelectConditionStep<Record11<LocalDate, LocalDateTime, EnumCamprole, LocalDateTime, LocalDateTime, Integer, Integer, String, String, String, String>> sql = jooq | ||||
| 		// @formatter:off | ||||
| 			.select(T_PERSON.BIRTHDATE, T_PERSON.CREATED, T_PERSON.CAMPROLE, | ||||
| 							T_CAMP.ARRIVE, T_CAMP.DEPART, | ||||
| 							T_CAMP.MIN_AGE, T_CAMP.MAX_AGE, | ||||
| 							T_PROFILE.FORENAME, T_PROFILE.SURNAME, | ||||
| 							REGISTRATOR.FORENAME, REGISTRATOR.SURNAME) | ||||
| 			.from(T_PERSON) | ||||
| 			.leftJoin(T_CAMP).on(T_CAMP.PK.eq(T_PERSON.FK_CAMP)) | ||||
| 			.leftJoin(T_PROFILE).on(T_PROFILE.PK.eq(T_PERSON.FK_PROFILE)) | ||||
| 			.leftJoin(REGISTRATOR).on(REGISTRATOR.PK.eq(T_PERSON.FK_REGISTRATOR)) | ||||
| 			.where(T_PERSON.PK.eq(pk)); | ||||
| 		// @formatter:on | ||||
| 		LOGGER.debug(sql.toString()); | ||||
| 		StringBuilder buf = new StringBuilder(); | ||||
| 		for (Record r : sql.fetch()) { | ||||
| 			LocalDate birthdate = r.get(T_PERSON.BIRTHDATE); | ||||
| 			LocalDateTime arrive = r.get(T_CAMP.ARRIVE); | ||||
| 			LocalDateTime depart = r.get(T_CAMP.DEPART); | ||||
| 			Integer minAge = r.get(T_CAMP.MIN_AGE); | ||||
| 			Integer maxAge = r.get(T_CAMP.MAX_AGE); | ||||
| 			String bookerForename = r.get(T_PROFILE.FORENAME); | ||||
| 			String bookerSurname = r.get(T_PROFILE.SURNAME); | ||||
| 			String registratorForename = r.get(REGISTRATOR.FORENAME); | ||||
| 			String registratorSurname = r.get(REGISTRATOR.SURNAME); | ||||
| 			LocalDateTime created = r.get(T_PERSON.CREATED); | ||||
| 			EnumCamprole role = r.get(T_PERSON.CAMPROLE); | ||||
| 			String createdString = created.format(DateTimeFormatter.ofPattern("dd.MM.yyyy")); | ||||
| 			buf.append(String.format("angemeldet von %s %s am %s\n", bookerForename, bookerSurname, createdString)); | ||||
| 			if (registratorForename != null || registratorSurname != null) { | ||||
| 				buf.append(String.format("bearbeitet von %s %s\n", registratorForename, registratorSurname)); | ||||
| 			} | ||||
| 			if (birthdate == null) { | ||||
| 				buf.append("Geburtstag wurde nicht angegeben\n"); | ||||
| 			} else if (arrive.isBefore(birthdate.atStartOfDay()) && depart.isAfter(birthdate.atStartOfDay())) { | ||||
| 				buf.append("hat während der Freizeit Geburtstag\n"); | ||||
| 			} | ||||
| 			if (birthdate == null || arrive == null) { | ||||
| 				buf.append("Alter konnte wegen fehlendem Geburtstag nicht ermittelt werden\n"); | ||||
| 			} else { | ||||
| 				long age = ChronoUnit.YEARS.between(birthdate, arrive); | ||||
| 				if (EnumCamprole.student.equals(role) && (minAge > age || maxAge < age)) { | ||||
| 					buf.append("passt vom Alter her nicht in die Freizeit"); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		return buf.toString(); | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,40 @@ | ||||
| package de.jottyfan.camporganizer.module.confirmation.person.impl; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.stereotype.Service; | ||||
|  | ||||
| import de.jottyfan.camporganizer.module.confirmation.person.IPersonService; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author jotty | ||||
|  * | ||||
|  */ | ||||
| @Service | ||||
| public class PersonService implements IPersonService { | ||||
| 	@Autowired | ||||
| 	private PersonGateway gateway; | ||||
|  | ||||
| 	@Override | ||||
| 	public PersonBean getPerson(String username, Integer pk) { | ||||
| 		return gateway.getPerson(username, pk); | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public Integer updatePerson(PersonBean bean) { | ||||
| 		return gateway.updatePerson(bean); | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public List<CampBean> getCamps(String username) { | ||||
| 		return gateway.getCamps(username); | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public String getAnnotations(Integer pk) { | ||||
| 		return gateway.getAnnotations(pk); | ||||
| 	} | ||||
|  | ||||
| } | ||||
| @@ -87,6 +87,49 @@ body { | ||||
| 	color: darkcyan; | ||||
| } | ||||
|  | ||||
| .badgetodo { | ||||
| 	border-radius: 8px; | ||||
| 	border: 1px solid black; | ||||
| 	color: white; | ||||
| 	font-weight: bolder; | ||||
| 	background-image: linear-gradient(to right bottom, rgb(153, 193, 241), rgb(26, 95, 180)); | ||||
| 	padding: 2px 4px 2px 4px; | ||||
| 	margin: 0px 2px 0px 2px; | ||||
| } | ||||
|  | ||||
| .badgewarn { | ||||
| 	border-radius: 8px; | ||||
| 	border: 1px solid black; | ||||
| 	color: white; | ||||
| 	font-weight: bolder; | ||||
| 	background-image: linear-gradient(to right bottom, #fa0, #a70); | ||||
| 	padding: 2px 4px 2px 4px; | ||||
| 	margin: 0px 2px 0px 2px; | ||||
| } | ||||
|  | ||||
| .badgeinfo { | ||||
| 	border-radius: 8px; | ||||
| 	border: 1px solid black; | ||||
| 	color: white; | ||||
| 	font-weight: bolder; | ||||
| 	background-image: linear-gradient(to right bottom, rgb(143, 240, 164), rgb(38, 162, 105)); | ||||
| 	padding: 2px 4px 2px 4px; | ||||
| 	margin: 0px 2px 0px 2px; | ||||
| } | ||||
|  | ||||
| .dist8 { | ||||
| 	margin: 8px; | ||||
| } | ||||
|  | ||||
| .error { | ||||
| 	color: red; | ||||
| } | ||||
|  | ||||
| .locked { | ||||
| 	background-color: rgba(255, 255, 255, 0.2) !important; | ||||
| 	cursor: not-allowed; | ||||
| } | ||||
|  | ||||
| .mytoggle_collapsed { | ||||
| 	display: none; | ||||
| } | ||||
|   | ||||
							
								
								
									
										169
									
								
								src/main/resources/templates/confirmation/confirmation.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								src/main/resources/templates/confirmation/confirmation.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,169 @@ | ||||
| <!DOCTYPE html> | ||||
| <html th:replace="~{template :: layout(~{::title}, ~{::libs}, ~{::header}, ~{::content})}" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security"> | ||||
| <head> | ||||
| <title>Camp Organizer Confirmation</title> | ||||
| <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | ||||
| <libs></libs> | ||||
| </head> | ||||
| <body> | ||||
| 	<header> | ||||
| 		<a th:href="@{/}" class="btn btn-secondary btn-icon-silent" title="zur Hauptseite"><i class="fas fa-home"></i></a> | ||||
| 		<a th:href="@{/confirmation}" class="btn btn-secondary btn-icon-silent" title="aktualisieren"><i class="fas fa-sync"></i></a> | ||||
| 	</header> | ||||
| 	<content> | ||||
| 	<div class="mainpage"> | ||||
| 		<div class="accordion" id="mainacc" sec:authorize="hasRole('registrator')"> | ||||
| 			<div class="accordion-item"> | ||||
| 				<h2 class="accordion-header" id="untouchedpanel"> | ||||
| 					<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#untoucheddiv" aria-expanded="true" aria-control="untoucheddiv">unbearbeitete Anmeldungen | ||||
| 					</button> | ||||
| 				</h2> | ||||
| 				<div id="untoucheddiv" class="accordion-collapse collapse show dist8" aria-labelled="untouchedpanel" data-bs-parent="#mainacc"> | ||||
| 					<table id="untouched"> | ||||
| 						<thead> | ||||
| 							<tr> | ||||
| 								<th>Freizeit</th> | ||||
| 								<th>Name</th> | ||||
| 								<th>Rolle</th> | ||||
| 								<th>Anmeldedatum</th> | ||||
| 							</tr> | ||||
| 						</thead> | ||||
| 						<tbody> | ||||
| 							<tr th:each="u : ${untouched}"> | ||||
| 								<td><a th:href="@{'/confirmation/person/' + ${u.pkPerson}}" th:text="${u.camp} + ' ' + ${#temporals.format(u.date, 'yyyy')}"></a></td> | ||||
| 								<td><a th:href="@{'/confirmation/person/' + ${u.pkPerson}}" th:text="${u.fullname}"></a></td> | ||||
| 								<td><a th:href="@{'/confirmation/person/' + ${u.pkPerson}}" th:text="${u.rolename}"></a></td> | ||||
| 								<td><a th:href="@{'/confirmation/person/' + ${u.pkPerson}}" th:text="${#temporals.format(u.registered, 'dd.MM.yyyy')}"></a></td> | ||||
| 							</tr> | ||||
| 						</tbody> | ||||
| 					</table> | ||||
| 					<script type="text/javascript"> | ||||
| 						$(document).ready(function() { | ||||
| 							$("#untouched").DataTable({ | ||||
| 								language : locale_de, | ||||
| 								pageLength : 5, | ||||
| 								lengthMenu : [ [ 5, 25, 50, -1 ], [ 5, 25, 50, "Alle" ] ] | ||||
| 							}); | ||||
| 						}); | ||||
| 					</script> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 			<div class="accordion-item"> | ||||
| 				<h2 class="accordion-header" id="approvedpanel"> | ||||
| 					<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#approveddiv" aria-expanded="true" aria-control="approveddiv">kürzlich bestätigte | ||||
| 						Anmeldungen</button> | ||||
| 				</h2> | ||||
| 				<div id="approveddiv" class="accordion-collapse collapse dist8" aria-labelled="approvedpanel" data-bs-parent="#mainacc"> | ||||
| 					<table id="approved"> | ||||
| 						<thead> | ||||
| 							<tr> | ||||
| 								<th>Freizeit</th> | ||||
| 								<th>Name</th> | ||||
| 								<th>Rolle</th> | ||||
| 								<th>Anmeldedatum</th> | ||||
| 							</tr> | ||||
| 						</thead> | ||||
| 						<tbody> | ||||
| 							<tr th:each="u : ${approved}"> | ||||
| 								<td><a th:href="@{'/confirmation/person/' + ${u.pkPerson}}" th:text="${u.camp} + ' ' + ${#temporals.format(u.date, 'yyyy')}"></a></td> | ||||
| 								<td><a th:href="@{'/confirmation/person/' + ${u.pkPerson}}" th:text="${u.fullname}"></a></td> | ||||
| 								<td><a th:href="@{'/confirmation/person/' + ${u.pkPerson}}" th:text="${u.rolename}"></a></td> | ||||
| 								<td><a th:href="@{'/confirmation/person/' + ${u.pkPerson}}" th:text="${#temporals.format(u.registered, 'dd.MM.yyyy')}"></a></td> | ||||
| 							</tr> | ||||
| 						</tbody> | ||||
| 					</table> | ||||
| 					<script type="text/javascript"> | ||||
| 						$(document).ready(function() { | ||||
| 							$("#approved").DataTable({ | ||||
| 								language : locale_de, | ||||
| 								pageLength : 5, | ||||
| 								lengthMenu : [ [ 5, 25, 50, -1 ], [ 5, 25, 50, "Alle" ] ] | ||||
| 							}); | ||||
| 						}); | ||||
| 					</script> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 			<div class="accordion-item"> | ||||
| 				<h2 class="accordion-header" id="rejectedpanel"> | ||||
| 					<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#rejecteddiv" aria-expanded="true" aria-control="rejecteddiv">kürzlich abgelehnte | ||||
| 						Anmeldungen</button> | ||||
| 				</h2> | ||||
| 				<div id="rejecteddiv" class="accordion-collapse collapse dist8" aria-labelled="rejectedpanel" data-bs-parent="#mainacc"> | ||||
| 					<table id="rejected"> | ||||
| 						<thead> | ||||
| 							<tr> | ||||
| 								<th>Freizeit</th> | ||||
| 								<th>Name</th> | ||||
| 								<th>Rolle</th> | ||||
| 								<th>Anmeldedatum</th> | ||||
| 							</tr> | ||||
| 						</thead> | ||||
| 						<tbody> | ||||
| 							<tr th:each="u : ${rejected}"> | ||||
| 								<td><a th:href="@{'/confirmation/person/' + ${u.pkPerson}}" th:text="${u.camp} + ' ' + ${#temporals.format(u.date, 'yyyy')}"></a></td> | ||||
| 								<td><a th:href="@{'/confirmation/person/' + ${u.pkPerson}}" th:text="${u.fullname}"></a></td> | ||||
| 								<td><a th:href="@{'/confirmation/person/' + ${u.pkPerson}}" th:text="${u.rolename}"></a></td> | ||||
| 								<td><a th:href="@{'/confirmation/person/' + ${u.pkPerson}}" th:text="${#temporals.format(u.registered, 'dd.MM.yyyy')}"></a></td> | ||||
| 							</tr> | ||||
| 						</tbody> | ||||
| 					</table> | ||||
| 					<script type="text/javascript"> | ||||
| 						$(document).ready(function() { | ||||
| 							$("#rejected").DataTable({ | ||||
| 								language : locale_de, | ||||
| 								pageLength : 5, | ||||
| 								lengthMenu : [ [ 5, 25, 50, -1 ], [ 5, 25, 50, "Alle" ] ] | ||||
| 							}); | ||||
| 						}); | ||||
| 					</script> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 			<div class="accordion-item"> | ||||
| 				<h2 class="accordion-header" id="overviewpanel"> | ||||
| 					<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#overviewdiv" aria-expanded="true" aria-control="overviewdiv">Freizeitenübersicht</button> | ||||
| 				</h2> | ||||
| 				<div id="overviewdiv" class="accordion-collapse collapse dist8" aria-labelled="overviewpanel" data-bs-parent="#mainacc"> | ||||
| 					<table id="campoverview"> | ||||
| 						<thead> | ||||
| 							<tr> | ||||
| 								<th>Freizeit</th> | ||||
| 								<th style="min-width: 64px">u/a/b</th> | ||||
| 							</tr> | ||||
| 						</thead> | ||||
| 						<tbody> | ||||
| 							<tr th:each="o : ${campoverview}"> | ||||
| 								<td th:text="${o.camp} + ' ' + ${#temporals.format(o.date, 'yyyy')}"></td> | ||||
| 								<td><span th:text="${o.untouched}" class="badgetodo"></span> / <span th:text="${o.rejected}" class="badgewarn"></span> / <span th:text="${o.approved}" class="badgeinfo"></span></td> | ||||
| 							</tr> | ||||
| 						</tbody> | ||||
| 						<tfoot> | ||||
| 							<tr> | ||||
| 								<td>Zusammenfassung</td> | ||||
| 								<td><span th:text="${campoverviewsummary.untouched}" class="badgetodo"></span> / <span th:text="${campoverviewsummary.rejected}" class="badgewarn"></span> / <span | ||||
| 									th:text="${campoverviewsummary.approved}" class="badgeinfo"></span></td> | ||||
| 						</tfoot> | ||||
| 					</table> | ||||
| 					<script type="text/javascript"> | ||||
| 						$(document).ready(function() { | ||||
| 							$("#campoverview").DataTable({ | ||||
| 								language : locale_de, | ||||
| 								pageLength : 5, | ||||
| 								lengthMenu : [ [ 5, 25, 50, -1 ], [ 5, 25, 50, "Alle" ] ] | ||||
| 							}); | ||||
| 						}); | ||||
| 					</script> | ||||
| 					<span>Legende:</span><span class="badgetodo">unbearbeitet</span><span class="badgewarn">abgelehnt</span><span class="badgeinfo">bestätigt</span> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 			<div class="accordion-item"> | ||||
| 				<h2 class="accordion-header" id="searchpanel"> | ||||
| 					<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#searchdiv" aria-expanded="true" aria-control="searchdiv">Suchmaske</button> | ||||
| 				</h2> | ||||
| 				<div id="searchdiv" class="accordion-collapse collapse dist8" aria-labelled="searchpanel" data-bs-parent="#mainacc">TODO: add an ajax based search field for persons to directly edit | ||||
| 					them</div> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 	</div> | ||||
| 	</content> | ||||
| </body> | ||||
| </html> | ||||
							
								
								
									
										135
									
								
								src/main/resources/templates/confirmation/person.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								src/main/resources/templates/confirmation/person.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,135 @@ | ||||
| <!DOCTYPE html> | ||||
| <html th:replace="~{template :: layout(~{::title}, ~{::libs}, ~{::header}, ~{::content})}" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security"> | ||||
| <head> | ||||
| <title>Camp Organizer Confirmation</title> | ||||
| <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | ||||
| <libs></libs> | ||||
| </head> | ||||
| <body> | ||||
| 	<header> | ||||
| 		<a th:href="@{/}" class="btn btn-secondary btn-icon-silent" title="zur Hauptseite"><i class="fas fa-home"></i></a> | ||||
| 		<a th:href="@{/confirmation}" class="btn btn-secondary btn-icon-silent" title="Anmeldungen"><i class="fas fa-user-check"></i></a> | ||||
| 		<a th:href="@{/confirmation/person/{pk}(pk=${person.pk})}" class="btn btn-secondary btn-icon-silent" title="aktualisieren" th:if="${person != null}"><i class="fas fa-sync"></i></a> | ||||
| 	</header> | ||||
| 	<content> | ||||
| 	<div class="mainpage"> | ||||
| 		<div class="container" style="max-width: 100%" sec:authorize="hasRole('registrator')"> | ||||
| 			<form action="#" th:action="@{/confirmation/person/update}" th:object="${person}" method="post" th:if="${person != null}"> | ||||
| 				<div class="row mb-2"> | ||||
| 					<label for="outputPk" class="col-sm-2 col-form-label">ID</label> | ||||
| 					<div class="col-sm-10"> | ||||
| 						<input type="text" th:field="*{pk}" class="outputPk form-control locked"></span> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 				<div class="row mb-2"> | ||||
| 					<label for="inputForename" class="col-sm-2 col-form-label">Vorname</label> | ||||
| 					<div class="col-sm-10"> | ||||
| 						<input type="text" th:field="*{forename}" class="inputForename form-control" /> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 				<div class="row mb-2"> | ||||
| 					<label for="inputSurname" class="col-sm-2 col-form-label">Nachname</label> | ||||
| 					<div class="col-sm-10"> | ||||
| 						<input type="text" th:field="*{surname}" class="inputSurname form-control" /> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 				<div class="row mb-2"> | ||||
| 					<label for="inputStreet" class="col-sm-2 col-form-label">Straße</label> | ||||
| 					<div class="col-sm-10"> | ||||
| 						<input type="text" th:field="*{street}" class="inputStreet form-control" /> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 				<div class="row mb-2"> | ||||
| 					<label for="inputZip" class="col-sm-2 col-form-label">PLZ</label> | ||||
| 					<div class="col-sm-10"> | ||||
| 						<input type="text" th:field="*{zip}" class="inputZip form-control" /> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 				<div class="row mb-2"> | ||||
| 					<label for="inputCity" class="col-sm-2 col-form-label">Ort</label> | ||||
| 					<div class="col-sm-10"> | ||||
| 						<input type="text" th:field="*{city}" class="inputCity form-control" /> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 				<div class="row mb-2"> | ||||
| 					<label for="inputBirthdate" class="col-sm-2 col-form-label">Geburtstag (TODO)</label> | ||||
| 					<div class="col-sm-10"> | ||||
| 						<input type="date" th:field="*{birthdate}" class="inputBirthdate form-control" /> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 				<div class="row mb-2"> | ||||
| 					<label for="inputSex" class="col-sm-2 col-form-label">Geschlecht</label> | ||||
| 					<div class="col-sm-10"> | ||||
| 						<select class="form-select" th:field="*{sex}"> | ||||
| 							<option value="male">männlich</option> | ||||
| 							<option value="female">weiblich</option> | ||||
| 						</select> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 				<div class="row mb-2"> | ||||
| 					<label for="inputPhone" class="col-sm-2 col-form-label">Telefon</label> | ||||
| 					<div class="col-sm-10"> | ||||
| 						<input type="text" th:field="*{phone}" class="inputPhone form-control" /> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 				<div class="row mb-2"> | ||||
| 					<label for="inputEmail" class="col-sm-2 col-form-label">E-Mail</label> | ||||
| 					<div class="col-sm-10"> | ||||
| 						<input type="email" th:field="*{email}" class="inputEmail form-control" /> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 				<div class="row mb-2"> | ||||
| 					<label for="outputCamp" class="col-sm-2 col-form-label">Freizeit</label> | ||||
| 					<div class="col-sm-10"> | ||||
| 						<select class="form-select locked" th:field="*{fkCamp}" disabled="disabled"> | ||||
| 							<option th:each="c : ${camps}" th:value="${c.pk}" th:text="${c.name} + ' ' + ${#temporals.format(c.arrive, 'yyyy')} + ' in ' + ${c.location}"></option> | ||||
| 						</select> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 				<div class="row mb-2"> | ||||
| 					<label for="outputCamprole" class="col-sm-2 col-form-label">Rolle</label> | ||||
| 					<div class="col-sm-10"> | ||||
| 						<select class="outputCamprole form-select locked" th:field="*{camprole}" disabled="disabled"> | ||||
| 							<option value="student">Teilnehmer</option> | ||||
| 							<option value="teacher">Mitarbeiter</option> | ||||
| 							<option value="director">Leiter</option> | ||||
| 							<option value="feeder">Küchenteam</option> | ||||
| 						</select> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 				<div class="row mb-2"> | ||||
| 					<label for="inputComment" class="col-sm-2 col-form-label">Kommentar</label> | ||||
| 					<div class="col-sm-10"> | ||||
| 						<textarea th:field="*{comment}" class="inputComment form-control"></textarea> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 				<div class="row mb-2"> | ||||
| 					<label for="outputAnno" class="col-sm-2 col-form-label">Anmerkungen</label> | ||||
| 					<div class="col-sm-10"> | ||||
| 						<pre class="form-control locked" th:text="${annotations}"></pre> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 				<div class="row mb-2"> | ||||
| 					<label for="inputAccept" class="col-sm-2 col-form-label">Status</label> | ||||
| 					<div class="col-sm-10"> | ||||
| 						<select class="form-select" th:field="*{accept}"> | ||||
| 							<option th:value="null">offen</option> | ||||
| 							<option th:value="true">bestätigt</option> | ||||
| 							<option th:value="false">abgelehnt</option> | ||||
| 						</select> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 				<div class="row mb-2"> | ||||
| 					<label for="inputAccept" class="col-sm-2 col-form-label"></label> | ||||
| 					<div class="col-sm-10"> | ||||
| 						<button type="submit" class="btn btn-primary">Ok</button> | ||||
| 						<a th:href="@{/confirmation/}" class="btn btn-secondary">Abbrechen</a> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 			</form> | ||||
| 			<div th:if="${person == null}" class="error">In der Datenbank wurde keine Person mit entsprechender ID gefunden.</div> | ||||
| 		</div> | ||||
| 	</div> | ||||
| 	</content> | ||||
| </body> | ||||
| </html> | ||||
| @@ -8,7 +8,8 @@ | ||||
| <body> | ||||
| 	<header> | ||||
| 		<a th:href="@{/}" class="btn btn-secondary btn-icon-silent" title="aktualisieren"><i class="fas fa-sync"></i></a> | ||||
| 		<a th:href="@{/business}" class="btn btn-secondary btn-icon-silent" title="Abrechnungen"><i class="far fa-money-bill-alt"></i></a> | ||||
| 		<a th:href="@{/business}" class="btn btn-secondary btn-icon-silent" title="Abrechnungen" sec:authorize="hasRole('business')"><i class="far fa-money-bill-alt"></i></a> | ||||
| 		<a th:href="@{/confirmation}" class="btn btn-secondary btn-icon-silent" title="Anmeldungen" sec:authorize="hasRole('registrator')"><i class="fas fa-user-check"></i></a> | ||||
| 		<a href="https://www.onkelwernerfreizeiten.de/nextcloud" class="btn btn-secondary btn-icon-silent" title="Nextcloud" target="_blank"><img th:src="@{images/nextcloud.png}" width="20px"/></a> | ||||
| 	</header> | ||||
| 	<content> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user