add IssueWork entity with Repository, Form and View

This commit is contained in:
Thomas Peetz
2025-06-04 21:36:26 +02:00
parent 4bb7d61f80
commit ea9f596abe
42 changed files with 491 additions and 87 deletions
+14
View File
@@ -105,12 +105,14 @@ class Issue(Base, BaseMixin):
volume = relationship("Volume", back_populates="issues")
story_arc_id = Column(String, ForeignKey("story_arc.id"), nullable=True)
story_arc = relationship("StoryArc", back_populates="issues")
issue_works = relationship("IssueWork")
class Artist(Base, BaseMixin):
__tablename__ = "artist"
name = Column(String, nullable=False)
weblink = Column(String, nullable=True)
comic_works = relationship("ComicWork")
issue_works = relationship("IssueWork")
def get_comics(self) -> Dict[str, List[str]]:
works: Dict[str, List[str]] = {}
@@ -128,6 +130,7 @@ class WorkType(Base, BaseMixin):
__tablename__ = "worktype"
name = Column(String, nullable=False, unique=True)
comic_works = relationship("ComicWork")
issue_works = relationship("IssueWork")
def get_artists(self) -> Dict[str, List[str]]:
works: Dict[str, List[str]] = {}
@@ -155,3 +158,14 @@ class ComicWork(Base, BaseMixin):
artist = relationship("Artist", back_populates="comic_works")
work_type_id = Column(String, ForeignKey("worktype.id"), nullable=False)
work_type = relationship("WorkType", back_populates="comic_works")
class IssueWork(Base, BaseMixin):
__tablename__ = "issue_work"
issue_id = Column(String, ForeignKey("issue.id"), nullable=False)
issue = relationship("Issue", back_populates="issue_works")
artist_id = Column(String, ForeignKey("artist.id"), nullable=False)
artist = relationship("Artist", back_populates="issue_works")
work_type_id = Column(String, ForeignKey("worktype.id"), nullable=False)
work_type = relationship("WorkType", back_populates="issue_works")
+2
View File
@@ -20,6 +20,7 @@ from db.models.comic import (
TradePaperback,
Volume,
ComicWork,
IssueWork,
Artist,
Comic,
Publisher,
@@ -61,6 +62,7 @@ registry = {
Issue.__tablename__: Issue,
TradePaperback.__tablename__: TradePaperback,
ComicWork.__tablename__: ComicWork,
IssueWork.__tablename__: IssueWork,
Article.__tablename__: Article,
BookshelfPublisher.__tablename__: BookshelfPublisher,
Book.__tablename__: Book,
+35
View File
@@ -195,6 +195,7 @@ class Issue(Base, BaseMixin):
volume = relationship("Volume", back_populates="issues")
story_arc_id = Column(String, ForeignKey("story_arc.id"), nullable=True)
story_arc = relationship("StoryArc", back_populates="issues")
issue_works = relationship("IssueWork")
def import_dict(self, import_data: Dict[AnyStr, Any]):
self.id = import_data['id']
@@ -236,6 +237,7 @@ class Artist(Base, BaseMixin):
name = Column(String, nullable=False)
weblink = Column(String, nullable=True)
comic_works = relationship("ComicWork")
issue_works = relationship("IssueWork")
def import_dict(self, import_data: Dict[AnyStr, Any]):
self.id = import_data['id']
@@ -262,6 +264,7 @@ class WorkType(Base, BaseMixin):
__tablename__ = "worktype"
name = Column(String, nullable=False, unique=True)
comic_works = relationship("ComicWork")
issue_works = relationship("IssueWork")
def __repr__(self):
return f'Worktype({self.id} {self.version} {self.name} {len(self.comic_works)})'
@@ -316,3 +319,35 @@ class ComicWork(Base, BaseMixin):
'work_type_id': self.work_type_id
}
return item
class IssueWork(Base, BaseMixin):
__tablename__ = "issue_work"
issue_id = Column(String, ForeignKey("issue.id"), nullable=False)
issue = relationship("Issue", back_populates="issue_works")
artist_id = Column(String, ForeignKey("artist.id"), nullable=False)
artist = relationship("Artist", back_populates="issue_works")
work_type_id = Column(String, ForeignKey("worktype.id"), nullable=False)
work_type = relationship("WorkType", back_populates="issue_works")
def import_dict(self, import_data: Dict[AnyStr, Any]):
self.id = import_data['id']
self.created_date = import_data['created_date']
self.last_modified_date = import_data['last_modified_date']
self.version = import_data['version']
self.issue_id = import_data['issue_id']
self.artist_id = import_data['artist_id']
self.work_type_id = import_data['work_type_id']
def export_dict(self) -> Dict[AnyStr, Any]:
item: Dict[AnyStr, Any] = {
'id': self.id,
'created_date': str(self.created_date),
'last_modified_date': str(self.last_modified_date),
'version': self.version,
'issue_id': self.issue_id,
'artist_id': self.artist_id,
'work_type_id': self.work_type_id
}
return item
@@ -3,15 +3,17 @@ package de.thpeetz.kontor.admin;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.sidenav.SideNavItem;
import com.vaadin.flow.router.RouterLink;
import de.thpeetz.kontor.admin.views.*;
import de.thpeetz.kontor.admin.views.AssignmentView;
import de.thpeetz.kontor.admin.views.PermissionView;
import de.thpeetz.kontor.admin.views.ProfileView;
import de.thpeetz.kontor.comics.ComicConstants;
import de.thpeetz.kontor.comics.views.ComicWorkView;
import de.thpeetz.kontor.comics.views.IssueWorkView;
import de.thpeetz.kontor.media.MediaConstants;
import de.thpeetz.kontor.media.views.MediaActorFileView;
public class AdminConstants {
private AdminConstants() {
// private constructor to hide the implicit public one
}
@@ -46,6 +48,7 @@ public class AdminConstants {
administration.addItem(new SideNavItem(PERMISSION, PermissionView.class));
SideNavItem data = new SideNavItem(DATA, ASSIGNMENT_ROUTE, VaadinIcon.DATABASE.create());
data.addItem(new SideNavItem(ComicConstants.COMICWORK, ComicWorkView.class));
data.addItem(new SideNavItem(ComicConstants.ISSUEWORK, IssueWorkView.class));
data.addItem(new SideNavItem(MediaConstants.MEDIAACTORFILE, MediaActorFileView.class));
data.addItem(new SideNavItem(AUTHORIZATION, AssignmentView.class));
administration.addItem(data);
@@ -9,6 +9,7 @@ import de.thpeetz.kontor.comics.views.ArtistView;
import de.thpeetz.kontor.comics.views.ComicView;
import de.thpeetz.kontor.comics.views.ComicWorkView;
import de.thpeetz.kontor.comics.views.IssueView;
import de.thpeetz.kontor.comics.views.IssueWorkView;
import de.thpeetz.kontor.comics.views.PublisherView;
import de.thpeetz.kontor.comics.views.StoryArcView;
import de.thpeetz.kontor.comics.views.TradePaperbackView;
@@ -25,7 +26,9 @@ public class ComicConstants {
public static final String PUBLISHER = "Publisher";
public static final String PUBLISHER_ROUTE = "comics/publisher";
public static final String COMICWORK = "ComicWork";
public static final String ISSUEWORK = "IssueWork";
public static final String COMICWORK_ROUTE = "comics/comicwork";
public static final String ISSUEWORK_ROUTE = "comics/issuework";
public static final String WORKTYPE = "Worktype";
public static final String WORKTYPE_ROUTE = "comics/worktype";
public static final String ARTIST = "Artist";
@@ -55,6 +58,10 @@ public class ComicConstants {
return new RouterLink(COMICWORK, ComicWorkView.class);
}
public static RouterLink getIssueWorkLink() {
return new RouterLink(ISSUEWORK, IssueWorkView.class);
}
public static RouterLink getIssueLink() {
return new RouterLink(ISSUE, IssueView.class);
}
@@ -1,30 +1,19 @@
package de.thpeetz.kontor.comics;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import de.thpeetz.kontor.comics.data.Artist;
import de.thpeetz.kontor.comics.data.ArtistRepository;
import de.thpeetz.kontor.comics.data.Comic;
import de.thpeetz.kontor.comics.data.ComicRepository;
import de.thpeetz.kontor.comics.data.ComicWork;
import de.thpeetz.kontor.comics.data.ComicWorkRepository;
import de.thpeetz.kontor.comics.data.Issue;
import de.thpeetz.kontor.comics.data.IssueRepository;
import de.thpeetz.kontor.comics.data.Publisher;
import de.thpeetz.kontor.comics.data.PublisherRepository;
import de.thpeetz.kontor.comics.data.StoryArc;
import de.thpeetz.kontor.comics.data.StoryArcRepository;
import de.thpeetz.kontor.comics.data.TradePaperback;
import de.thpeetz.kontor.comics.data.TradePaperbackRepository;
import de.thpeetz.kontor.comics.data.Volume;
import de.thpeetz.kontor.comics.data.VolumeRepository;
import de.thpeetz.kontor.comics.data.Worktype;
import de.thpeetz.kontor.comics.data.WorktypeRepository;
import de.thpeetz.kontor.comics.repository.ArtistRepository;
import de.thpeetz.kontor.comics.repository.ComicRepository;
import de.thpeetz.kontor.comics.repository.ComicWorkRepository;
import de.thpeetz.kontor.comics.repository.IssueRepository;
import de.thpeetz.kontor.comics.repository.PublisherRepository;
import de.thpeetz.kontor.comics.repository.StoryArcRepository;
import de.thpeetz.kontor.comics.repository.TradePaperbackRepository;
import de.thpeetz.kontor.comics.repository.VolumeRepository;
import de.thpeetz.kontor.comics.repository.WorktypeRepository;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@@ -37,6 +37,10 @@ public class Artist extends AbstractEntity {
@Nullable
List<ComicWork> comicWorks = new LinkedList<>();
@OneToMany(fetch = FetchType.EAGER, mappedBy = "artist", cascade = CascadeType.REFRESH, orphanRemoval = true)
@Nullable
List<IssueWork> issueWorks = new LinkedList<>();
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("Artist{");
@@ -5,19 +5,16 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import de.thpeetz.kontor.common.data.*;
import io.hypersistence.utils.hibernate.type.basic.YearMonthDateType;
import jakarta.annotation.Nullable;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.annotations.Type;
import java.time.YearMonth;
import java.util.*;
@Getter
@Setter
@@ -58,6 +55,10 @@ public class Issue extends AbstractEntity {
private Boolean inStock;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "issue", cascade = CascadeType.REFRESH, orphanRemoval = true)
@Nullable
List<IssueWork> issueWorks;
public String getComicTitle() {
return comic.getTitle();
}
@@ -0,0 +1,48 @@
package de.thpeetz.kontor.comics.data;
import de.thpeetz.kontor.common.data.AbstractEntity;
import jakarta.persistence.Entity;
import jakarta.persistence.Index;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import jakarta.validation.constraints.NotNull;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@EqualsAndHashCode(callSuper = false)
@Entity
@Table(indexes = {@Index(columnList = "issue_id, artist_id, workType_id") },
uniqueConstraints = @UniqueConstraint(columnNames = {"issue_id", "artist_id", "workType_id" })
)
public class IssueWork extends AbstractEntity {
@ManyToOne
@JoinColumn(name = "issue_id")
@NotNull
private Issue issue;
@ManyToOne
@JoinColumn(name = "artist_id")
@NotNull
private Artist artist;
@ManyToOne
@JoinColumn(name = "workType_id")
@NotNull
private Worktype workType;
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("IssueWork{");
sb.append("issue=").append(issue);
sb.append(", artist=").append(artist);
sb.append(", workType=").append(workType);
sb.append('}');
return sb.toString();
}
}
@@ -34,6 +34,10 @@ public class Worktype extends AbstractEntity {
@Nullable
List<ComicWork> comicWorks = new LinkedList<>();
@OneToMany(fetch = FetchType.EAGER, mappedBy = "workType", cascade = CascadeType.REFRESH, orphanRemoval = true)
@Nullable
List<IssueWork> issueWorks = new LinkedList<>();
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("Worktype{");
@@ -1,7 +1,8 @@
package de.thpeetz.kontor.comics.data;
package de.thpeetz.kontor.comics.repository;
import java.util.List;
import de.thpeetz.kontor.comics.data.*;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
@@ -1,7 +1,8 @@
package de.thpeetz.kontor.comics.data;
package de.thpeetz.kontor.comics.repository;
import java.util.List;
import de.thpeetz.kontor.comics.data.*;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
@@ -1,7 +1,8 @@
package de.thpeetz.kontor.comics.data;
package de.thpeetz.kontor.comics.repository;
import java.util.List;
import de.thpeetz.kontor.comics.data.*;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
@@ -1,7 +1,8 @@
package de.thpeetz.kontor.comics.data;
package de.thpeetz.kontor.comics.repository;
import java.util.List;
import de.thpeetz.kontor.comics.data.*;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
@@ -0,0 +1,15 @@
package de.thpeetz.kontor.comics.repository;
import de.thpeetz.kontor.comics.data.*;
import org.springframework.data.jpa.repository.*;
import java.util.*;
public interface IssueWorkRepository extends JpaRepository<IssueWork, String> {
@Query("SELECT i from IssueWork i where i.issue = ?1 and i.artist = ?2 and i.workType = ?3")
IssueWork findbyIssueAndArtistAndWorktype(Issue issue, Artist artist, Worktype worktype);
@Query("select i from IssueWork i where i.issue = ?1")
List<IssueWork> findByIssue(Issue issue);
}
@@ -1,7 +1,8 @@
package de.thpeetz.kontor.comics.data;
package de.thpeetz.kontor.comics.repository;
import java.util.List;
import de.thpeetz.kontor.comics.data.*;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
@@ -1,7 +1,8 @@
package de.thpeetz.kontor.comics.data;
package de.thpeetz.kontor.comics.repository;
import java.util.List;
import de.thpeetz.kontor.comics.data.*;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
@@ -1,7 +1,8 @@
package de.thpeetz.kontor.comics.data;
package de.thpeetz.kontor.comics.repository;
import java.util.List;
import de.thpeetz.kontor.comics.data.*;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
@@ -1,7 +1,8 @@
package de.thpeetz.kontor.comics.data;
package de.thpeetz.kontor.comics.repository;
import java.util.List;
import de.thpeetz.kontor.comics.data.*;
import org.springframework.data.jpa.repository.JpaRepository;
public interface VolumeRepository extends JpaRepository<Volume, String> {
@@ -1,7 +1,8 @@
package de.thpeetz.kontor.comics.data;
package de.thpeetz.kontor.comics.repository;
import java.util.List;
import de.thpeetz.kontor.comics.data.*;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
@@ -2,26 +2,10 @@ package de.thpeetz.kontor.comics.services;
import java.util.List;
import de.thpeetz.kontor.comics.data.*;
import de.thpeetz.kontor.comics.repository.*;
import org.springframework.stereotype.Service;
import de.thpeetz.kontor.comics.data.Artist;
import de.thpeetz.kontor.comics.data.ArtistRepository;
import de.thpeetz.kontor.comics.data.Comic;
import de.thpeetz.kontor.comics.data.ComicRepository;
import de.thpeetz.kontor.comics.data.ComicWork;
import de.thpeetz.kontor.comics.data.ComicWorkRepository;
import de.thpeetz.kontor.comics.data.Issue;
import de.thpeetz.kontor.comics.data.IssueRepository;
import de.thpeetz.kontor.comics.data.Publisher;
import de.thpeetz.kontor.comics.data.PublisherRepository;
import de.thpeetz.kontor.comics.data.StoryArc;
import de.thpeetz.kontor.comics.data.StoryArcRepository;
import de.thpeetz.kontor.comics.data.TradePaperback;
import de.thpeetz.kontor.comics.data.TradePaperbackRepository;
import de.thpeetz.kontor.comics.data.Volume;
import de.thpeetz.kontor.comics.data.VolumeRepository;
import de.thpeetz.kontor.comics.data.Worktype;
import de.thpeetz.kontor.comics.data.WorktypeRepository;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@@ -35,12 +19,13 @@ public class ComicService {
private final StoryArcRepository storyArcRepository;
private final TradePaperbackRepository tradePaperbackRepository;
private final ComicWorkRepository comicWorkRepository;
private final IssueWorkRepository issueWorkRepository;
private final VolumeRepository volumeRepository;
private final WorktypeRepository worktypeRepository;
public ComicService(PublisherRepository publisherRepository, ComicRepository comicRepository,
ArtistRepository artistRepository, IssueRepository issueRepository, StoryArcRepository storyArcRepository,
TradePaperbackRepository tradePaperbackRepository, ComicWorkRepository comicWorkRepository,
TradePaperbackRepository tradePaperbackRepository, ComicWorkRepository comicWorkRepository, IssueWorkRepository issueWorkRepository,
VolumeRepository volumeRepository, WorktypeRepository worktypeRepository) {
this.publisherRepository = publisherRepository;
@@ -50,6 +35,7 @@ public class ComicService {
this.storyArcRepository = storyArcRepository;
this.tradePaperbackRepository = tradePaperbackRepository;
this.comicWorkRepository = comicWorkRepository;
this.issueWorkRepository = issueWorkRepository;
this.volumeRepository = volumeRepository;
this.worktypeRepository = worktypeRepository;
}
@@ -231,6 +217,31 @@ public class ComicService {
comicWorkRepository.delete(comicWork);
}
public List<IssueWork> findAllIssueWorks() {
return issueWorkRepository.findAll();
}
public void saveIssueWork(IssueWork issueWork) {
if (issueWork == null) {
log.warn("IssueWork is null. Are you sure you have connected your form to the application?");
return;
}
issueWorkRepository.save(issueWork);
}
public void deleteIssueWork(IssueWork issueWork) {
Issue issue = issueWork.getIssue();
issue.getIssueWorks().remove(issueWork);
issueRepository.save(issue);
Artist artist = issueWork.getArtist();
artist.getIssueWorks().remove(issueWork);
artistRepository.save(artist);
Worktype worktype = issueWork.getWorkType();
worktype.getIssueWorks().remove(issueWork);
worktypeRepository.save(worktype);
issueWorkRepository.delete(issueWork);
}
public List<Volume> findAllVolumes() {
return volumeRepository.findAll();
}
@@ -285,14 +296,23 @@ public class ComicService {
public void deleteWorktype(Worktype worktype) {
List<ComicWork> comicWorks = worktype.getComicWorks();
List<IssueWork> issueWorks = worktype.getIssueWorks();
if (comicWorks == null) {
log.warn("reference to ComicWork is null");
return;
}
} else {
log.info("found {} references to ComicWork", comicWorks.size());
comicWorks.forEach(comicWork -> {
comicWork.setWorkType(null);
});
}
if (issueWorks == null) {
log.warn("reference to ComicWork is null");
} else {
log.info("found {} references to IssueWork", issueWorks.size());
issueWorks.forEach(issueWork -> {
issueWork.setWorkType(null);
});
}
log.info("delete Worktype: {}", worktype);
worktypeRepository.delete(worktype);
}
@@ -10,14 +10,13 @@ import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.checkbox.Checkbox;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.formlayout.FormLayout;
import com.vaadin.flow.component.grid.*;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.binder.BeanValidationBinder;
import com.vaadin.flow.data.binder.Binder;
import de.thpeetz.kontor.comics.data.Comic;
import de.thpeetz.kontor.comics.data.Issue;
import de.thpeetz.kontor.comics.data.Volume;
import de.thpeetz.kontor.comics.data.*;
import de.thpeetz.kontor.common.views.YearMonthField;
import lombok.extern.slf4j.Slf4j;
@@ -31,6 +30,7 @@ public class IssueForm extends FormLayout {
YearMonthField publishedOn = new YearMonthField();
Checkbox isRead = new Checkbox("Read");
Checkbox inStock = new Checkbox("In stock");
Grid<IssueWork> issueWorks = new Grid<>(IssueWork.class);
Button save = new Button("Save");
Button delete = new Button("Delete");
@@ -53,7 +53,11 @@ public class IssueForm extends FormLayout {
comic.setItemLabelGenerator(Comic::getTitle);
volume.setItems(volumes);
volume.setItemLabelGenerator(Volume::getName);
add(comic, volume, issueNumber, title, publishedOn, isRead, inStock, createButtonsLayout());
issueWorks.setColumns("workType.name", "artist.name");
issueWorks.getColumnByKey("workType.name").setHeader("Work type");
issueWorks.getColumnByKey("artist.name").setHeader("Artist");
issueWorks.getColumns().forEach(col -> col.setAutoWidth(true));
add(comic, volume, issueNumber, title, publishedOn, isRead, inStock,issueWorks, createButtonsLayout());
}
private HorizontalLayout createButtonsLayout() {
@@ -96,6 +100,11 @@ public class IssueForm extends FormLayout {
}
}
public void setIssueWorks(List<IssueWork> works) {
log.info("Setting issue works: {}", works);
issueWorks.setItems(works);
}
public abstract static class IssueFormEvent extends ComponentEvent<IssueForm> {
@lombok.Getter
private final Issue issue;
@@ -156,6 +156,12 @@ public class IssueView extends VerticalLayout {
closeEditor();
} else {
form.setIssue(issue);
if (issue.getIssueWorks() == null) {
log.info("No issue works");
} else {
log.info("Issue works sze: {}", issue.getIssueWorks().size());
}
form.setIssueWorks(issue.getIssueWorks());
form.setVisible(true);
addClassName("editing");
}
@@ -0,0 +1,113 @@
package de.thpeetz.kontor.comics.views;
import java.util.List;
import com.vaadin.flow.component.ComponentEvent;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.Key;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.formlayout.FormLayout;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.data.binder.BeanValidationBinder;
import com.vaadin.flow.data.binder.Binder;
import de.thpeetz.kontor.comics.data.Artist;
import de.thpeetz.kontor.comics.data.Issue;
import de.thpeetz.kontor.comics.data.IssueWork;
import de.thpeetz.kontor.comics.data.Worktype;
public class IssueWorkForm extends FormLayout {
ComboBox<Issue> issue = new ComboBox<>("Issue");
ComboBox<Artist> artist = new ComboBox<>("Artist");
ComboBox<Worktype> workType = new ComboBox<>("Worktype");
Button save = new Button("Save");
Button delete = new Button("Delete");
Button close = new Button("Cancel");
Binder<IssueWork> binder = new BeanValidationBinder<>(IssueWork.class);
public IssueWorkForm(List<Issue> issues, List<Artist> artists, List<Worktype> workTypes) {
addClassName("issuework-form");
binder.bindInstanceFields(this);
issue.setItems(issues);
issue.setItemLabelGenerator(Issue::getIssueNumber);
artist.setItems(artists);
artist.setItemLabelGenerator(Artist::getName);
workType.setItems(workTypes);
workType.setItemLabelGenerator(Worktype::getName);
add(issue, artist, workType, createButtonsLayout());
}
private HorizontalLayout createButtonsLayout() {
save.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
delete.addThemeVariants(ButtonVariant.LUMO_ERROR);
close.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
save.addClickShortcut(Key.ENTER);
close.addClickShortcut(Key.ESCAPE);
save.addClickListener(event -> validateAndSave());
delete.addClickListener(event -> fireEvent(new DeleteEvent(this, binder.getBean())));
close.addClickListener(event -> fireEvent(new CloseEvent(this)));
binder.addStatusChangeListener(e -> save.setEnabled(binder.isValid()));
return new HorizontalLayout(save, delete, close);
}
private void validateAndSave() {
if (binder.isValid()) {
fireEvent(new SaveEvent(this, binder.getBean()));
}
}
public void setIssueWork(IssueWork issueWork) {
binder.setBean(issueWork);
}
public abstract static class IssueWorkFormEvent extends ComponentEvent<IssueWorkForm> {
private IssueWork issueWork;
protected IssueWorkFormEvent(IssueWorkForm source, IssueWork issueWork) {
super(source, false);
this.issueWork = issueWork;
}
public IssueWork getIssueWork() {
return issueWork;
}
}
public static class SaveEvent extends IssueWorkFormEvent {
SaveEvent(IssueWorkForm source, IssueWork issueWork) {
super(source, issueWork);
}
}
public static class DeleteEvent extends IssueWorkFormEvent {
DeleteEvent(IssueWorkForm source, IssueWork issueWork) {
super(source, issueWork);
}
}
public static class CloseEvent extends IssueWorkFormEvent {
CloseEvent(IssueWorkForm source) {
super(source, null);
}
}
public void addDeleteListener(ComponentEventListener<DeleteEvent> listener) {
addListener(DeleteEvent.class, listener);
}
public void addSaveListener(ComponentEventListener<SaveEvent> listener) {
addListener(SaveEvent.class, listener);
}
public void addCloseListener(ComponentEventListener<CloseEvent> listener) {
addListener(CloseEvent.class, listener);
}
}
@@ -0,0 +1,122 @@
package de.thpeetz.kontor.comics.views;
import de.thpeetz.kontor.comics.data.IssueWork;
import org.springframework.context.annotation.Scope;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import de.thpeetz.kontor.comics.ComicConstants;
import de.thpeetz.kontor.comics.services.ComicService;
import de.thpeetz.kontor.common.views.MainLayout;
import jakarta.annotation.security.PermitAll;
@SpringComponent
@Scope("prototype")
@PermitAll
@Route(value = ComicConstants.ISSUEWORK_ROUTE, layout = MainLayout.class)
@PageTitle("IssueWork | Comics | Kontor")
public class IssueWorkView extends VerticalLayout {
Grid<IssueWork> grid = new Grid<>(IssueWork.class);
IssueWorkForm form;
ComicService service;
public IssueWorkView(ComicService service) {
this.service = service;
addClassName("issueWork-view");
setSizeFull();
configureGrid();
configureForm();
add(getToolbar(), getContent());
updateList();
}
public Grid<IssueWork> getGrid() {
return grid;
}
private void configureGrid() {
grid.addClassName("issue-grid");
grid.setSizeFull();
grid.setColumns("issue.title", "artist.name", "workType.name");
grid.getColumns().forEach(col -> col.setAutoWidth(true));
grid.asSingleSelect().addValueChangeListener(event -> editIssueWork(event.getValue()));
}
public IssueWorkForm getForm() {
return form;
}
private void configureForm() {
form = new IssueWorkForm(service.findAllIssues(), service.findAllArtists(null),
service.findAllWorktypes(null));
form.setWidth("25em");
form.setVisible(false);
form.addSaveListener(this::saveIssueWork);
form.addDeleteListener(this::deleteIssueWork);
form.addCloseListener(e -> closeEditor());
}
private void saveIssueWork(IssueWorkForm.SaveEvent event) {
service.saveIssueWork(event.getIssueWork());
updateList();
closeEditor();
}
private void deleteIssueWork(IssueWorkForm.DeleteEvent event) {
service.deleteIssueWork(event.getIssueWork());
updateList();
closeEditor();
}
private Component getContent() {
HorizontalLayout content = new HorizontalLayout(grid, form);
content.setFlexGrow(2, grid);
content.setFlexGrow(1, form);
content.addClassName("content");
content.setSizeFull();
return content;
}
private HorizontalLayout getToolbar() {
Button addIssueButton = new Button("Add IssueWork");
addIssueButton.addClickListener(click -> addIssueWork());
HorizontalLayout toolbar = new HorizontalLayout(addIssueButton);
toolbar.addClassName("toolbar");
return toolbar;
}
public void editIssueWork(IssueWork issueWork) {
if (issueWork == null) {
closeEditor();
} else {
form.setIssueWork(issueWork);
form.setVisible(true);
addClassName("editing");
}
}
private void closeEditor() {
form.setIssueWork(null);
form.setVisible(false);
removeClassName("editing");
}
private void addIssueWork() {
grid.asSingleSelect().clear();
editIssueWork(new IssueWork());
}
public void updateList() {
grid.setItems(service.findAllIssueWorks());
}
}
@@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.*;
import java.util.List;
import de.thpeetz.kontor.comics.repository.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@@ -4,8 +4,7 @@ import static org.junit.jupiter.api.Assertions.*;
import java.util.List;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import de.thpeetz.kontor.comics.repository.*;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
@@ -13,7 +12,6 @@ import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.transaction.TransactionSystemException;
import de.thpeetz.kontor.comics.TestConstants;
@@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.*;
import java.util.List;
import de.thpeetz.kontor.comics.repository.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@@ -2,6 +2,7 @@ package de.thpeetz.kontor.comics.data;
import static org.junit.jupiter.api.Assertions.*;
import de.thpeetz.kontor.comics.repository.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@@ -2,8 +2,7 @@ package de.thpeetz.kontor.comics.data;
import static org.junit.jupiter.api.Assertions.*;
import java.util.List;
import de.thpeetz.kontor.comics.repository.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@@ -2,6 +2,7 @@ package de.thpeetz.kontor.comics.data;
import static org.junit.jupiter.api.Assertions.*;
import de.thpeetz.kontor.comics.repository.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@@ -2,8 +2,7 @@ package de.thpeetz.kontor.comics.data;
import static org.junit.jupiter.api.Assertions.*;
import java.util.List;
import de.thpeetz.kontor.comics.repository.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.*;
import java.util.List;
import de.thpeetz.kontor.comics.repository.*;
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
@@ -11,7 +12,6 @@ import org.junit.jupiter.api.TestMethodOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.transaction.TransactionSystemException;
import de.thpeetz.kontor.comics.TestConstants;
@@ -1,9 +1,10 @@
package de.thpeetz.kontor.comics.data;
package de.thpeetz.kontor.comics.repository;
import static org.junit.jupiter.api.Assertions.*;
import java.util.List;
import de.thpeetz.kontor.comics.data.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
import org.junit.jupiter.api.Order;
@@ -1,10 +1,11 @@
package de.thpeetz.kontor.comics.data;
package de.thpeetz.kontor.comics.repository;
import static org.junit.jupiter.api.Assertions.*;
import java.util.List;
import java.util.stream.Collectors;
import de.thpeetz.kontor.comics.data.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@@ -1,7 +1,8 @@
package de.thpeetz.kontor.comics.data;
package de.thpeetz.kontor.comics.repository;
import static org.junit.jupiter.api.Assertions.*;
import de.thpeetz.kontor.comics.data.*;
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
@@ -1,4 +1,4 @@
package de.thpeetz.kontor.comics.data;
package de.thpeetz.kontor.comics.repository;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -6,6 +6,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.util.List;
import de.thpeetz.kontor.comics.data.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@@ -1,9 +1,10 @@
package de.thpeetz.kontor.comics.data;
package de.thpeetz.kontor.comics.repository;
import static org.junit.jupiter.api.Assertions.*;
import java.util.List;
import de.thpeetz.kontor.comics.data.*;
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
@@ -1,9 +1,10 @@
package de.thpeetz.kontor.comics.data;
package de.thpeetz.kontor.comics.repository;
import static org.junit.jupiter.api.Assertions.*;
import java.util.List;
import de.thpeetz.kontor.comics.data.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@@ -1,4 +1,4 @@
package de.thpeetz.kontor.comics.data;
package de.thpeetz.kontor.comics.repository;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -6,6 +6,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.List;
import de.thpeetz.kontor.comics.data.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@@ -1,9 +1,10 @@
package de.thpeetz.kontor.comics.data;
package de.thpeetz.kontor.comics.repository;
import static org.junit.jupiter.api.Assertions.*;
import java.util.List;
import de.thpeetz.kontor.comics.data.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@@ -1,20 +1,15 @@
package de.thpeetz.kontor.comics.data;
package de.thpeetz.kontor.comics.repository;
import static org.junit.jupiter.api.Assertions.*;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
import org.junit.jupiter.api.Order;
import de.thpeetz.kontor.comics.data.*;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import de.thpeetz.kontor.comics.TestConstants;
@SpringBootTest
class WorktypeRepositoryTest {