From 0e9088dc1c5186746ca7575cbdcd474aa0214072 Mon Sep 17 00:00:00 2001 From: Jottyfan Date: Sat, 8 Oct 2022 16:42:21 +0200 Subject: [PATCH] integrated module confirmation --- .../camporganizer/config/SecurityConfig.java | 2 +- .../business/business/BusinessController.java | 2 +- .../privileges/PrivilegesController.java | 2 +- .../confirmation/ConfirmationController.java | 47 +++ .../confirmation/IConfirmationService.java | 55 ++++ .../confirmation/impl/BookingBean.java | 113 +++++++ .../confirmation/impl/CampOverviewBean.java | 88 ++++++ .../impl/ConfirmationGateway.java | 162 ++++++++++ .../impl/ConfirmationService.java | 53 ++++ .../confirmation/person/IPersonService.java | 47 +++ .../confirmation/person/PersonController.java | 48 +++ .../confirmation/person/impl/CampBean.java | 75 +++++ .../confirmation/person/impl/PersonBean.java | 276 ++++++++++++++++++ .../person/impl/PersonGateway.java | 205 +++++++++++++ .../person/impl/PersonService.java | 40 +++ src/main/resources/static/css/style.css | 43 +++ .../templates/confirmation/confirmation.html | 169 +++++++++++ .../templates/confirmation/person.html | 135 +++++++++ src/main/resources/templates/dashboard.html | 3 +- 19 files changed, 1561 insertions(+), 4 deletions(-) create mode 100644 src/main/java/de/jottyfan/camporganizer/module/confirmation/confirmation/ConfirmationController.java create mode 100644 src/main/java/de/jottyfan/camporganizer/module/confirmation/confirmation/IConfirmationService.java create mode 100644 src/main/java/de/jottyfan/camporganizer/module/confirmation/confirmation/impl/BookingBean.java create mode 100644 src/main/java/de/jottyfan/camporganizer/module/confirmation/confirmation/impl/CampOverviewBean.java create mode 100644 src/main/java/de/jottyfan/camporganizer/module/confirmation/confirmation/impl/ConfirmationGateway.java create mode 100644 src/main/java/de/jottyfan/camporganizer/module/confirmation/confirmation/impl/ConfirmationService.java create mode 100644 src/main/java/de/jottyfan/camporganizer/module/confirmation/person/IPersonService.java create mode 100644 src/main/java/de/jottyfan/camporganizer/module/confirmation/person/PersonController.java create mode 100644 src/main/java/de/jottyfan/camporganizer/module/confirmation/person/impl/CampBean.java create mode 100644 src/main/java/de/jottyfan/camporganizer/module/confirmation/person/impl/PersonBean.java create mode 100644 src/main/java/de/jottyfan/camporganizer/module/confirmation/person/impl/PersonGateway.java create mode 100644 src/main/java/de/jottyfan/camporganizer/module/confirmation/person/impl/PersonService.java create mode 100644 src/main/resources/templates/confirmation/confirmation.html create mode 100644 src/main/resources/templates/confirmation/person.html diff --git a/src/main/java/de/jottyfan/camporganizer/config/SecurityConfig.java b/src/main/java/de/jottyfan/camporganizer/config/SecurityConfig.java index 2dad836..e47a91b 100644 --- a/src/main/java/de/jottyfan/camporganizer/config/SecurityConfig.java +++ b/src/main/java/de/jottyfan/camporganizer/config/SecurityConfig.java @@ -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(); diff --git a/src/main/java/de/jottyfan/camporganizer/module/business/business/BusinessController.java b/src/main/java/de/jottyfan/camporganizer/module/business/business/BusinessController.java index c388b35..92bd34a 100644 --- a/src/main/java/de/jottyfan/camporganizer/module/business/business/BusinessController.java +++ b/src/main/java/de/jottyfan/camporganizer/module/business/business/BusinessController.java @@ -9,7 +9,7 @@ import org.springframework.web.bind.annotation.GetMapping; /** * - * @author henkej + * @author jotty * */ @Controller diff --git a/src/main/java/de/jottyfan/camporganizer/module/business/privileges/PrivilegesController.java b/src/main/java/de/jottyfan/camporganizer/module/business/privileges/PrivilegesController.java index cfd2672..c7c443c 100644 --- a/src/main/java/de/jottyfan/camporganizer/module/business/privileges/PrivilegesController.java +++ b/src/main/java/de/jottyfan/camporganizer/module/business/privileges/PrivilegesController.java @@ -16,7 +16,7 @@ import de.jottyfan.camporganizer.module.business.privileges.impl.PrivilegesBean; /** * - * @author henkej + * @author jotty * */ @Controller diff --git a/src/main/java/de/jottyfan/camporganizer/module/confirmation/confirmation/ConfirmationController.java b/src/main/java/de/jottyfan/camporganizer/module/confirmation/confirmation/ConfirmationController.java new file mode 100644 index 0000000..91d4cd4 --- /dev/null +++ b/src/main/java/de/jottyfan/camporganizer/module/confirmation/confirmation/ConfirmationController.java @@ -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 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"; + } +} diff --git a/src/main/java/de/jottyfan/camporganizer/module/confirmation/confirmation/IConfirmationService.java b/src/main/java/de/jottyfan/camporganizer/module/confirmation/confirmation/IConfirmationService.java new file mode 100644 index 0000000..49944fc --- /dev/null +++ b/src/main/java/de/jottyfan/camporganizer/module/confirmation/confirmation/IConfirmationService.java @@ -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 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 getUntouched(HttpServletRequest request); + + /** + * get a list of approved bookings + * + * @param request the request + * @return the list of approved bookings + */ + public List getApproved(HttpServletRequest request); + + /** + * get a list of rejected bookings + * + * @param request the request + * @return the list of rejected bookings + */ + public List getRejected(HttpServletRequest request); +} diff --git a/src/main/java/de/jottyfan/camporganizer/module/confirmation/confirmation/impl/BookingBean.java b/src/main/java/de/jottyfan/camporganizer/module/confirmation/confirmation/impl/BookingBean.java new file mode 100644 index 0000000..8bfc781 --- /dev/null +++ b/src/main/java/de/jottyfan/camporganizer/module/confirmation/confirmation/impl/BookingBean.java @@ -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 { + 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; + } +} diff --git a/src/main/java/de/jottyfan/camporganizer/module/confirmation/confirmation/impl/CampOverviewBean.java b/src/main/java/de/jottyfan/camporganizer/module/confirmation/confirmation/impl/CampOverviewBean.java new file mode 100644 index 0000000..5487ca9 --- /dev/null +++ b/src/main/java/de/jottyfan/camporganizer/module/confirmation/confirmation/impl/CampOverviewBean.java @@ -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 { + 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; + } +} diff --git a/src/main/java/de/jottyfan/camporganizer/module/confirmation/confirmation/impl/ConfirmationGateway.java b/src/main/java/de/jottyfan/camporganizer/module/confirmation/confirmation/impl/ConfirmationGateway.java new file mode 100644 index 0000000..eeeb1f6 --- /dev/null +++ b/src/main/java/de/jottyfan/camporganizer/module/confirmation/confirmation/impl/ConfirmationGateway.java @@ -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 getCampOverviewBeans(String currentUser) { + Name COUNT = DSL.name("count"); + SelectHavingStep> 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 map = new HashMap<>(); + for (Record4 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 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 getListWithCondition(String currentUser, Condition condition) { + SelectSeekStep1, 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 list = new ArrayList<>(); + for (Record7 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 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 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 getRejected(String currentUser) { + return getListWithCondition(currentUser, T_PERSON.ACCEPT.isFalse()); + } +} diff --git a/src/main/java/de/jottyfan/camporganizer/module/confirmation/confirmation/impl/ConfirmationService.java b/src/main/java/de/jottyfan/camporganizer/module/confirmation/confirmation/impl/ConfirmationService.java new file mode 100644 index 0000000..027f143 --- /dev/null +++ b/src/main/java/de/jottyfan/camporganizer/module/confirmation/confirmation/impl/ConfirmationService.java @@ -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 getCampOverview(HttpServletRequest request) { + return gateway.getCampOverviewBeans(getCurrentUser(request)); + } + + @Override + public List getUntouched(HttpServletRequest request) { + return gateway.getUntouched(getCurrentUser(request)); + } + + @Override + public List getApproved(HttpServletRequest request) { + return gateway.getApproved(getCurrentUser(request)); + } + + @Override + public List getRejected(HttpServletRequest request) { + return gateway.getRejected(getCurrentUser(request)); + } +} diff --git a/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/IPersonService.java b/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/IPersonService.java new file mode 100644 index 0000000..a628db7 --- /dev/null +++ b/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/IPersonService.java @@ -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 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); +} diff --git a/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/PersonController.java b/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/PersonController.java new file mode 100644 index 0000000..e4651c6 --- /dev/null +++ b/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/PersonController.java @@ -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"; + } +} diff --git a/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/impl/CampBean.java b/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/impl/CampBean.java new file mode 100644 index 0000000..b5bd99b --- /dev/null +++ b/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/impl/CampBean.java @@ -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; + } +} diff --git a/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/impl/PersonBean.java b/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/impl/PersonBean.java new file mode 100644 index 0000000..195acaf --- /dev/null +++ b/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/impl/PersonBean.java @@ -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; + } +} diff --git a/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/impl/PersonGateway.java b/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/impl/PersonGateway.java new file mode 100644 index 0000000..efa4888 --- /dev/null +++ b/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/impl/PersonGateway.java @@ -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 getCamps(String username) { + SelectSeekStep1, 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 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 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 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> 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(); + } +} diff --git a/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/impl/PersonService.java b/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/impl/PersonService.java new file mode 100644 index 0000000..7bb35d8 --- /dev/null +++ b/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/impl/PersonService.java @@ -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 getCamps(String username) { + return gateway.getCamps(username); + } + + @Override + public String getAnnotations(Integer pk) { + return gateway.getAnnotations(pk); + } + +} diff --git a/src/main/resources/static/css/style.css b/src/main/resources/static/css/style.css index a682909..655baeb 100644 --- a/src/main/resources/static/css/style.css +++ b/src/main/resources/static/css/style.css @@ -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; } diff --git a/src/main/resources/templates/confirmation/confirmation.html b/src/main/resources/templates/confirmation/confirmation.html new file mode 100644 index 0000000..a1c53d2 --- /dev/null +++ b/src/main/resources/templates/confirmation/confirmation.html @@ -0,0 +1,169 @@ + + + +Camp Organizer Confirmation + + + + +
+ + +
+ +
+
+
+

+ +

+
+ + + + + + + + + + + + + + + + + +
FreizeitNameRolleAnmeldedatum
+ +
+
+
+

+ +

+
+ + + + + + + + + + + + + + + + + +
FreizeitNameRolleAnmeldedatum
+ +
+
+
+

+ +

+
+ + + + + + + + + + + + + + + + + +
FreizeitNameRolleAnmeldedatum
+ +
+
+
+

+ +

+
+ + + + + + + + + + + + + + + + + + +
Freizeitu/a/b
/ /
Zusammenfassung / /
+ + Legende:unbearbeitetabgelehntbestätigt +
+
+
+

+ +

+
TODO: add an ajax based search field for persons to directly edit + them
+
+
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/confirmation/person.html b/src/main/resources/templates/confirmation/person.html new file mode 100644 index 0000000..af68a8a --- /dev/null +++ b/src/main/resources/templates/confirmation/person.html @@ -0,0 +1,135 @@ + + + +Camp Organizer Confirmation + + + + +
+ + + +
+ +
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+

+					
+
+
+ +
+ +
+
+
+ +
+ + Abbrechen +
+
+
+
In der Datenbank wurde keine Person mit entsprechender ID gefunden.
+
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/dashboard.html b/src/main/resources/templates/dashboard.html index 9346ab2..d349d26 100644 --- a/src/main/resources/templates/dashboard.html +++ b/src/main/resources/templates/dashboard.html @@ -8,7 +8,8 @@
- + +