diff --git a/pom.xml b/pom.xml index 32c2f70..9959344 100644 --- a/pom.xml +++ b/pom.xml @@ -93,6 +93,11 @@ border-layout-addon 1.0 + + org.vaadin.ui + numberfield + 0.2.0 + org.hibernate diff --git a/src/main/java/META-INF/persistence.xml b/src/main/java/META-INF/persistence.xml index 812e1a8..4cd9bc6 100644 --- a/src/main/java/META-INF/persistence.xml +++ b/src/main/java/META-INF/persistence.xml @@ -14,9 +14,11 @@ de.kreth.vaadin.clubhelper.vaadinclubhelper.data.Startpass de.kreth.vaadin.clubhelper.vaadinclubhelper.data.StartpassStartrechte de.kreth.vaadin.clubhelper.vaadinclubhelper.data.Version + de.kreth.vaadin.clubhelper.vaadinclubhelper.data.Pflicht + de.kreth.vaadin.clubhelper.vaadinclubhelper.data.Altersgruppe - + diff --git a/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/business/EventBusiness.java b/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/business/EventBusiness.java index 5225f5c..a5e5c6a 100644 --- a/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/business/EventBusiness.java +++ b/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/business/EventBusiness.java @@ -1,5 +1,7 @@ package de.kreth.vaadin.clubhelper.vaadinclubhelper.business; +import java.time.LocalDateTime; +import java.util.Date; import java.util.List; import java.util.Set; @@ -8,7 +10,9 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import de.kreth.vaadin.clubhelper.vaadinclubhelper.dao.AltersgruppeDao; import de.kreth.vaadin.clubhelper.vaadinclubhelper.dao.ClubEventDao; +import de.kreth.vaadin.clubhelper.vaadinclubhelper.data.Altersgruppe; import de.kreth.vaadin.clubhelper.vaadinclubhelper.data.ClubEvent; import de.kreth.vaadin.clubhelper.vaadinclubhelper.data.Person; @@ -20,6 +24,9 @@ public class EventBusiness { @Autowired ClubEventDao dao; + @Autowired + AltersgruppeDao altersgruppeDao; + private ClubEvent current; public synchronized List loadEvents() { @@ -48,4 +55,30 @@ public class EventBusiness { } } } + + public Altersgruppe createAltersgruppe() { + Altersgruppe e = new Altersgruppe(); + e.setStart(LocalDateTime.now().getYear() - 1); + e.setEnd(LocalDateTime.now().getYear()); + e.setChanged(new Date()); + e.setCreated(e.getChanged()); + Set altersgruppen = current.getAltersgruppen(); + if (altersgruppen.contains(e)) { + for (Altersgruppe el : altersgruppen) { + if (el.equals(e)) { + return el; + } + } + } else { + altersgruppen.add(e); + e.setClubEvent(current); + } + + return e; + } + + public void storeAltersgruppe(Altersgruppe edited) { + altersgruppeDao.save(edited); + dao.update(current); + } } diff --git a/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/dao/AbstractDaoImpl.java b/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/dao/AbstractDaoImpl.java index 2f9feb4..775ded8 100644 --- a/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/dao/AbstractDaoImpl.java +++ b/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/dao/AbstractDaoImpl.java @@ -23,7 +23,12 @@ public abstract class AbstractDaoImpl implements IDao { @Override @Transactional public void save(T obj) { - em.persist(obj); + + if (em.contains(obj)) { + em.merge(obj); + } else { + em.persist(obj); + } } @Override diff --git a/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/dao/AltersgruppeDao.java b/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/dao/AltersgruppeDao.java new file mode 100644 index 0000000..5289f3b --- /dev/null +++ b/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/dao/AltersgruppeDao.java @@ -0,0 +1,7 @@ +package de.kreth.vaadin.clubhelper.vaadinclubhelper.dao; + +import de.kreth.vaadin.clubhelper.vaadinclubhelper.data.Altersgruppe; + +public interface AltersgruppeDao extends IDao { + +} diff --git a/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/dao/AltersgruppeDaoImpl.java b/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/dao/AltersgruppeDaoImpl.java new file mode 100644 index 0000000..3d13e77 --- /dev/null +++ b/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/dao/AltersgruppeDaoImpl.java @@ -0,0 +1,14 @@ +package de.kreth.vaadin.clubhelper.vaadinclubhelper.dao; + +import org.springframework.stereotype.Repository; + +import de.kreth.vaadin.clubhelper.vaadinclubhelper.data.Altersgruppe; + +@Repository +public class AltersgruppeDaoImpl extends AbstractDaoImpl implements AltersgruppeDao { + + public AltersgruppeDaoImpl() { + super(Altersgruppe.class); + } + +} diff --git a/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/data/Altersgruppe.java b/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/data/Altersgruppe.java index 2f484ee..a0df821 100644 --- a/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/data/Altersgruppe.java +++ b/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/data/Altersgruppe.java @@ -20,6 +20,8 @@ public class Altersgruppe extends BaseEntity implements Serializable { private String bezeichnung; private int start; private int end; + + @ManyToOne private Pflicht pflicht; @ManyToOne @JoinColumn(name = "event_id") diff --git a/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/data/ClubEvent.java b/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/data/ClubEvent.java index e1ca824..1a67709 100644 --- a/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/data/ClubEvent.java +++ b/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/data/ClubEvent.java @@ -9,6 +9,7 @@ import java.util.Set; import javax.persistence.Entity; import javax.persistence.Id; +import javax.persistence.ManyToMany; import javax.persistence.Transient; import org.vaadin.addon.calendar.item.BasicItem; @@ -25,6 +26,7 @@ public class ClubEvent extends BasicItem { private String iCalUID; private String organizerDisplayName; + @ManyToMany private Set persons; private Set altersgruppen; diff --git a/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/ui/EventDetails.java b/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/ui/EventDetails.java index 7fac5e8..018bafc 100644 --- a/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/ui/EventDetails.java +++ b/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/ui/EventDetails.java @@ -9,8 +9,9 @@ import com.vaadin.ui.GridLayout; import de.kreth.vaadin.clubhelper.vaadinclubhelper.business.EventBusiness; import de.kreth.vaadin.clubhelper.vaadinclubhelper.dao.GroupDao; import de.kreth.vaadin.clubhelper.vaadinclubhelper.dao.PersonDao; +import de.kreth.vaadin.clubhelper.vaadinclubhelper.dao.PflichtenDao; import de.kreth.vaadin.clubhelper.vaadinclubhelper.data.ClubEvent; -import de.kreth.vaadin.clubhelper.vaadinclubhelper.data.Person; +import de.kreth.vaadin.clubhelper.vaadinclubhelper.ui.components.EventAltersgruppen; import de.kreth.vaadin.clubhelper.vaadinclubhelper.ui.components.PersonGrid; import de.kreth.vaadin.clubhelper.vaadinclubhelper.ui.components.SingleEventView; @@ -22,23 +23,26 @@ public class EventDetails extends GridLayout implements NamedView { private final EventBusiness eventBusiness; private final PersonDao personDao; private final GroupDao groupDao; + private final PflichtenDao pflichtenDao; - private Person loggedinPerson; private ClubEvent currentEvent; private SingleEventView eventView; private PersonGrid personGrid; + private EventAltersgruppen eventAltersgruppen; - public EventDetails(PersonDao personDao, GroupDao groupDao, EventBusiness eventBusiness) { + public EventDetails(PersonDao personDao, GroupDao groupDao, EventBusiness eventBusiness, + PflichtenDao pflichtenDao) { super(3, 5); this.eventBusiness = eventBusiness; this.personDao = personDao; this.groupDao = groupDao; + this.pflichtenDao = pflichtenDao; } @Override public void enter(ViewChangeEvent event) { Navigator navigator = event.getNavigator(); - loggedinPerson = (Person) getSession().getAttribute(Person.SESSION_LOGIN); + currentEvent = eventBusiness.getCurrent(); eventView = new SingleEventView(); @@ -50,12 +54,15 @@ public class EventDetails extends GridLayout implements NamedView { personGrid.hideFilter(); personGrid.setSelectionMode(SelectionMode.NONE); + eventAltersgruppen = new EventAltersgruppen(pflichtenDao, eventBusiness); + Button back = new Button("Zurück"); back.addClickListener(ev -> navigator.navigateTo(((NamedView) event.getOldView()).getViewName())); - addComponent(eventView, 0, 0, 1, 0); - addComponent(personGrid, 0, 1, 1, 1); - addComponent(back, 1, 4); + addComponent(eventView, 0, 0); + addComponent(eventAltersgruppen, 1, 0); + addComponent(personGrid, 2, 0); + addComponent(back, 0, 4); setSizeFull(); } diff --git a/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/ui/components/EventAltersgruppen.java b/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/ui/components/EventAltersgruppen.java new file mode 100644 index 0000000..3e47ed4 --- /dev/null +++ b/src/main/java/de/kreth/vaadin/clubhelper/vaadinclubhelper/ui/components/EventAltersgruppen.java @@ -0,0 +1,240 @@ +package de.kreth.vaadin.clubhelper.vaadinclubhelper.ui.components; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.vaadin.ui.NumberField; + +import com.vaadin.data.Binder; +import com.vaadin.data.Binder.Binding; +import com.vaadin.data.HasValue.ValueChangeEvent; +import com.vaadin.data.ValidationResult; +import com.vaadin.data.ValueContext; +import com.vaadin.data.provider.DataProvider; +import com.vaadin.data.provider.ListDataProvider; +import com.vaadin.data.validator.AbstractValidator; +import com.vaadin.event.selection.SelectionEvent; +import com.vaadin.shared.ui.ValueChangeMode; +import com.vaadin.ui.Button; +import com.vaadin.ui.ComboBox; +import com.vaadin.ui.Component; +import com.vaadin.ui.CustomField; +import com.vaadin.ui.Grid; +import com.vaadin.ui.Grid.SelectionMode; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.TextField; +import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.components.grid.Editor; +import com.vaadin.ui.components.grid.EditorSaveEvent; + +import de.kreth.vaadin.clubhelper.vaadinclubhelper.business.EventBusiness; +import de.kreth.vaadin.clubhelper.vaadinclubhelper.dao.PflichtenDao; +import de.kreth.vaadin.clubhelper.vaadinclubhelper.data.Altersgruppe; +import de.kreth.vaadin.clubhelper.vaadinclubhelper.data.Pflicht; + +public class EventAltersgruppen extends VerticalLayout { + + private static final long serialVersionUID = -7777374233838542085L; + private final Logger LOGGER = LoggerFactory.getLogger(getClass()); + + private static final int OLDEST_YEAR = 1900; + private static final int CURRENT_YEAR = LocalDateTime.now().getYear(); + + private final Grid gruppen; + private final ListDataProvider provider; + private final Binder binder; + private final EventBusiness eventSupplier; + + public EventAltersgruppen(PflichtenDao pflichtenDao, EventBusiness eventBusiness) { + + setCaption("Altersklassen"); + this.eventSupplier = eventBusiness; + Button addButton = new Button("Hinzufügen"); + addButton.addClickListener(ev -> addGruppe()); + + HorizontalLayout buttonLayout = new HorizontalLayout(addButton); + + provider = DataProvider.ofCollection(new ArrayList()); + + gruppen = new Grid<>(); + gruppen.setDataProvider(provider); + binder = gruppen.getEditor().getBinder(); + + setupGrid(pflichtenDao); + + addComponents(buttonLayout, gruppen); + } + + void setupGrid(PflichtenDao pflichtenDao) { + + Binding pflichtBinding = binder.bind(createPflichtenCombo(pflichtenDao.listAll()), + Altersgruppe::getPflicht, Altersgruppe::setPflicht); + Binding bezBinding = binder.bind(createBezeichnungField("Altersgruppe.Bezeichnung"), + Altersgruppe::getBezeichnung, Altersgruppe::setBezeichnung); + + IntNumberField vonField = new IntNumberField("Von", "Altersgruppe.Start"); + Binding bindingStart = binder.forField(vonField).withValidator(new YearValidator()) + .bind(Altersgruppe::getStart, Altersgruppe::setStart); + Binding bindingEnd = binder.forField(new IntNumberField("Bis", "Altersgruppe.End")) + .withValidator(new BisYearValidator(vonField)).bind(Altersgruppe::getEnd, Altersgruppe::setEnd); + vonField.addValueChangeListener(ev -> bindingEnd.validate()); + + gruppen.addColumn(Altersgruppe::getBezeichnung).setCaption("Bezeichnung").setEditorBinding(bezBinding); + gruppen.addColumn(Altersgruppe::getStart).setCaption("Von").setEditorBinding(bindingStart); + gruppen.addColumn(Altersgruppe::getEnd).setCaption("Bis").setEditorBinding(bindingEnd); + gruppen.addColumn(Altersgruppe::getPflicht).setCaption("Pflicht").setEditorBinding(pflichtBinding); + + binder.addValueChangeListener(this::valueChange); + gruppen.addSelectionListener(this::selectionChange); + + Editor editor = gruppen.getEditor(); + editor.setEnabled(true); + + editor.addSaveListener(ev -> gridRowSaved(ev)); + gruppen.setSelectionMode(SelectionMode.SINGLE); + } + + private void gridRowSaved(EditorSaveEvent ev) { + binder.validate(); + if (binder.isValid()) { + Altersgruppe edited = binder.getBean(); + validateAndStore(edited); + } + provider.refreshAll(); + } + + void selectionChange(SelectionEvent event) { + Optional selected = event.getFirstSelectedItem(); + if (selected.isPresent()) { + binder.setBean(selected.get()); + } else { + binder.setBean(null); + } + provider.refreshAll(); + } + + void valueChange(ValueChangeEvent event) { + + Component component = event.getComponent(); + Object value = event.getValue(); + Object oldValue = event.getOldValue(); + String componentId = component.getId(); + + LOGGER.trace("Changed: {}, value={}, old={}", componentId, value, oldValue); + } + + public void validateAndStore(Altersgruppe edited) { + if (edited.getBezeichnung() != null && !edited.getBezeichnung().isBlank() && edited.getPflicht() != null + && edited.getClubEvent() != null) { + eventSupplier.storeAltersgruppe(edited); + LOGGER.info("Stored: {}", edited); + } + } + + TextField createBezeichnungField(String id) { + TextField textField = new TextField(); + textField.setId(id); + return textField; + } + + ComboBox createPflichtenCombo(List pflichtenList) { + ComboBox pflichten = new ComboBox<>(); + pflichten.setId("Altersgruppe.Pflicht"); + pflichten.setItems(pflichtenList); + return pflichten; + } + + private void addGruppe() { + + Altersgruppe e = eventSupplier.createAltersgruppe(); + provider.getItems().add(e); + binder.setBean(e); + provider.refreshAll(); + } + + private static class YearValidator extends AbstractValidator { + + private static final long serialVersionUID = 450137530250464249L; + private static final String ERROR_MESSAGE = String.format("Jahreszahl muss zwischen %d und %d liegen", + OLDEST_YEAR, CURRENT_YEAR); + + protected YearValidator() { + super(ERROR_MESSAGE); + } + + @Override + public ValidationResult apply(Integer value, ValueContext context) { + if (value >= OLDEST_YEAR && value <= CURRENT_YEAR) { + return ValidationResult.ok(); + } + + return ValidationResult.error(ERROR_MESSAGE); + } + + } + + private static class BisYearValidator extends YearValidator { + + private static final long serialVersionUID = -7197197454502399416L; + final IntNumberField vonField; + + public BisYearValidator(IntNumberField vonField) { + super(); + this.vonField = vonField; + } + + @Override + public ValidationResult apply(Integer value, ValueContext context) { + if (value > vonField.getValue()) { + return super.apply(value, context); + } else { + return ValidationResult.error("Bis Wert muss größer als Von Wert sein!"); + } + } + } + + private class IntNumberField extends CustomField { + + private static final long serialVersionUID = 2221967167572584942L; + private final NumberField field; + + public IntNumberField(String caption, String id) { + field = new NumberField(); + field.setDecimalAllowed(false); + field.setDecimalSeparatorAlwaysShown(false); + field.setGroupingUsed(false); + field.setNegativeAllowed(false); + field.setPlaceholder(String.format("Jahr zwischen 1900 und %d", CURRENT_YEAR)); + field.setValueChangeMode(ValueChangeMode.LAZY); + field.setValueChangeTimeout(300); + field.setMinValue(OLDEST_YEAR); + field.setMaxValue(CURRENT_YEAR); + field.setCaption(caption); + field.setId(id); + } + + @Override + public Integer getValue() { + String value = field.getValue(); + if (value == null || value.isBlank()) { + return Integer.valueOf(0); + } + return Double.valueOf(value).intValue(); + } + + @Override + protected Component initContent() { + return field; + } + + @Override + protected void doSetValue(Integer value) { + field.setValue(value.doubleValue()); + } + + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 9882abc..cf5f159 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,9 +1,7 @@ -spring.datasource.url=jdbc:mysql://localhost:3306/clubhelper?useUnicode=yes&characterEncoding=utf8&serverTimezone=Europe/Berlin +spring.datasource.url=jdbc:mysql://localhost:3306/clubhelper?useUnicode=yes;characterEncoding=utf8 +#;serverTimezone=Europe/Berlin spring.datasource.username=markus spring.datasource.password=0773 -#security.ignored=/** -#security.basic.enable=false -#management.security.enabled=false spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy diff --git a/src/main/resources/hibernate.cfg.xml b/src/main/resources/hibernate.cfg.xml index a41d1d7..5de93bd 100644 --- a/src/main/resources/hibernate.cfg.xml +++ b/src/main/resources/hibernate.cfg.xml @@ -5,18 +5,9 @@ - - true true true - - org.hibernate.context.internal.JTASessionContext - diff --git a/src/main/resources/schema/ClubEvent.hbm.xml b/src/main/resources/schema/ClubEvent.hbm.xml index d00cf00..e3335d8 100644 --- a/src/main/resources/schema/ClubEvent.hbm.xml +++ b/src/main/resources/schema/ClubEvent.hbm.xml @@ -30,7 +30,7 @@ - + diff --git a/src/main/resources/simplelogger.properties b/src/main/resources/simplelogger.properties index e24ccea..8e65753 100644 --- a/src/main/resources/simplelogger.properties +++ b/src/main/resources/simplelogger.properties @@ -3,6 +3,6 @@ org.slf4j.simpleLogger.logFile=System.err org.slf4j.simpleLogger.showThreadName=false org.slf4j.simpleLogger.log.de.kreth.vaadin.clubhelper=trace -org.slf4j.simpleLogger.log.org.hibernate=info -org.slf4j.simpleLogger.log.com.zaxxer.hikari=info -org.slf4j.simpleLogger.log.com.mysql=info \ No newline at end of file +org.slf4j.simpleLogger.log.org=info +org.slf4j.simpleLogger.log.com=info +org.slf4j.simpleLogger.log.net=info \ No newline at end of file