From 9476d8853e5f079a3ef31493fb45c370e3d6b1fc Mon Sep 17 00:00:00 2001 From: Thomas Peetz Date: Mon, 24 Aug 2026 12:55:05 +0200 Subject: [PATCH] add MediaLofiView --- .../kontor/constants/MediaConstants.java | 4 + .../kontor/data/common/AbstractEntity.java | 8 +- .../thpeetz/kontor/data/media/MediaFile.java | 1 + .../kontor/views/media/MediaLofiForm.java | 113 ++++++++++++++ .../kontor/views/media/MediaLofiView.java | 144 ++++++++++++++++++ 5 files changed, 269 insertions(+), 1 deletion(-) create mode 100644 kontor-spring/src/main/java/de/thpeetz/kontor/views/media/MediaLofiForm.java create mode 100644 kontor-spring/src/main/java/de/thpeetz/kontor/views/media/MediaLofiView.java diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/constants/MediaConstants.java b/kontor-spring/src/main/java/de/thpeetz/kontor/constants/MediaConstants.java index bf99a97..6edcd0e 100644 --- a/kontor-spring/src/main/java/de/thpeetz/kontor/constants/MediaConstants.java +++ b/kontor-spring/src/main/java/de/thpeetz/kontor/constants/MediaConstants.java @@ -6,6 +6,7 @@ import com.vaadin.flow.component.sidenav.SideNavItem; import de.thpeetz.kontor.views.media.MediaActorView; import de.thpeetz.kontor.views.media.MediaArticleView; import de.thpeetz.kontor.views.media.MediaFileView; +import de.thpeetz.kontor.views.media.MediaLofiView; import de.thpeetz.kontor.views.media.MediaVideoView; import java.util.ArrayList; @@ -15,6 +16,7 @@ public class MediaConstants { public static final String MEDIA = "Media"; public static final String MEDIAFILE_ROUTE = "media/mediafile"; public static final String MEDIAVIDEO_ROUTE = "media/mediavideo"; + public static final String MEDIALOFI_ROUTE = "media/medialofi"; public static final String MEDIAARTICLE_ROUTE = "media/mediaarticle"; public static final String MEDIA_ROLE = "ROLE_MEDIA"; public static final String MEDIAACTOR_ROUTE = "media/mediaactor"; @@ -22,6 +24,7 @@ public class MediaConstants { public static final String MEDIAACTORFILE = "Media Actor Files"; private static final String MEDIAFILE = "Media Files"; private static final String MEDIAVIDEO = "Media Videos"; + private static final String MEDIALOFI = "Media Lofi"; private static final String MEDIAARTICLE = "Media Article"; private static final String MEDIAACTOR = "Media Actor"; @@ -34,6 +37,7 @@ public class MediaConstants { public static SideNavItem getMediaNavigation(ArrayList roles) { SideNavItem media = new SideNavItem(MEDIA, MEDIAFILE_ROUTE, VaadinIcon.VIMEO.create()); media.addItem(new SideNavItem(MEDIAVIDEO, MediaVideoView.class)); + media.addItem(new SideNavItem(MEDIALOFI, MediaLofiView.class)); media.addItem(new SideNavItem(MEDIAARTICLE, MediaArticleView.class)); if (roles.contains(MEDIA_ROLE)) { media.addItem(new SideNavItem(MEDIAFILE, MediaFileView.class)); diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/data/common/AbstractEntity.java b/kontor-spring/src/main/java/de/thpeetz/kontor/data/common/AbstractEntity.java index 5c01a78..2706102 100644 --- a/kontor-spring/src/main/java/de/thpeetz/kontor/data/common/AbstractEntity.java +++ b/kontor-spring/src/main/java/de/thpeetz/kontor/data/common/AbstractEntity.java @@ -1,6 +1,5 @@ package de.thpeetz.kontor.data.common; -import jakarta.persistence.*; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; @@ -9,6 +8,13 @@ import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.Version; + import java.util.Date; @Slf4j diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/data/media/MediaFile.java b/kontor-spring/src/main/java/de/thpeetz/kontor/data/media/MediaFile.java index 951830d..03f771e 100644 --- a/kontor-spring/src/main/java/de/thpeetz/kontor/data/media/MediaFile.java +++ b/kontor-spring/src/main/java/de/thpeetz/kontor/data/media/MediaFile.java @@ -27,6 +27,7 @@ public class MediaFile extends AbstractEntity { private boolean shouldDownload; @Nullable + @Column(length = 300) private String title; @Nullable diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/views/media/MediaLofiForm.java b/kontor-spring/src/main/java/de/thpeetz/kontor/views/media/MediaLofiForm.java new file mode 100644 index 0000000..0b1234f --- /dev/null +++ b/kontor-spring/src/main/java/de/thpeetz/kontor/views/media/MediaLofiForm.java @@ -0,0 +1,113 @@ +package de.thpeetz.kontor.views.media; + +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.checkbox.Checkbox; +import com.vaadin.flow.component.formlayout.FormLayout; +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.data.media.MediaLofi; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class MediaLofiForm extends FormLayout { + + TextField url = new TextField("URL"); + TextField title = new TextField("Title"); + TextField fileName = new TextField("Dateiname"); + Checkbox review = new Checkbox("Review"); + Checkbox shouldDownload = new Checkbox("Download"); + + Button save = new Button("Save"); + Button delete = new Button("Delete"); + Button close = new Button("Cancel"); + + Binder binder = new BeanValidationBinder<>(MediaLofi.class); + + + public MediaLofiForm() { + addClassName("medialofi-form"); + binder.bindInstanceFields(this); + add(url, 2); + add(title, 2); + add(fileName, 2); + add(review, shouldDownload, 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(event -> { + save.setEnabled(event.getBinder().isValid()); + }); + return new HorizontalLayout(save, delete, close); + } + + private void validateAndSave() { + if (binder.isValid()) { + fireEvent(new SaveEvent(this, binder.getBean())); + } + } + + public void setMediaLofi(MediaLofi mediaLofi) { + binder.setBean(mediaLofi); + } + + public abstract static class MediaLofiFormEvent extends ComponentEvent { + private MediaLofi mediaLofi; + + protected MediaLofiFormEvent(MediaLofiForm source, MediaLofi mediaLofi) { + super(source, false); + this.mediaLofi = mediaLofi; + } + + public MediaLofi getMediaLofi() { + return mediaLofi; + } + } + + public static class SaveEvent extends MediaLofiFormEvent { + SaveEvent(MediaLofiForm source, MediaLofi mediaLofi) { + super(source, mediaLofi); + } + } + + public static class DeleteEvent extends MediaLofiFormEvent { + DeleteEvent(MediaLofiForm source, MediaLofi mediaLofi) { + super(source, mediaLofi); + } + } + + public static class CloseEvent extends MediaLofiFormEvent { + CloseEvent(MediaLofiForm source) { + super(source, null); + } + } + + public void addDeleteListener(ComponentEventListener listener) { + addListener(DeleteEvent.class, listener); + } + + public void addSaveListener(ComponentEventListener listener) { + addListener(SaveEvent.class, listener); + } + + public void addCloseListener(ComponentEventListener listener) { + addListener(CloseEvent.class, listener); + } +} diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/views/media/MediaLofiView.java b/kontor-spring/src/main/java/de/thpeetz/kontor/views/media/MediaLofiView.java new file mode 100644 index 0000000..0eb8121 --- /dev/null +++ b/kontor-spring/src/main/java/de/thpeetz/kontor/views/media/MediaLofiView.java @@ -0,0 +1,144 @@ +package de.thpeetz.kontor.views.media; + +import org.springframework.context.annotation.Scope; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.button.ButtonVariant; +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.constants.MediaConstants; +import de.thpeetz.kontor.data.media.MediaLofi; +import de.thpeetz.kontor.services.MediaLofiService; +import de.thpeetz.kontor.views.common.ColumnToggleContextMenu; +import de.thpeetz.kontor.views.common.MainLayout; +import de.thpeetz.kontor.views.common.SearchFilterField; +import de.thpeetz.kontor.views.common.StatusIcon; +import jakarta.annotation.security.PermitAll; +import lombok.Getter; + +@SpringComponent +@Scope("prototype") +@PermitAll +@Route(value = MediaConstants.MEDIALOFI_ROUTE, layout = MainLayout.class) +@PageTitle("MediaLoFi | Media | Kontor") +public class MediaLofiView extends VerticalLayout { + + @Getter + Grid grid = new Grid<>(MediaLofi.class, false); + Grid.Column idColumn = grid.addColumn(MediaLofi::getId).setHeader("ID").setResizable(true); + Grid.Column urlColumn = grid.addColumn(MediaLofi::getUrl).setHeader("URL").setResizable(true).setSortable(true); + Grid.Column titleColumn = grid.addColumn(MediaLofi::getTitle).setHeader("Titel").setResizable(true).setSortable(true); + Grid.Column fileNameColumn = grid.addColumn(MediaLofi::getFileName).setHeader("Dateiname").setResizable(true).setSortable(true); + Grid.Column reviewColumn = grid.addComponentColumn(mediaLofi -> StatusIcon.create(mediaLofi.isReview())) + .setHeader("Überprüfung") + .setWidth("6rem"); + Grid.Column shouldDownloadColumn = grid.addComponentColumn(mediaLofi -> StatusIcon.create(mediaLofi.isShouldDownload())) + .setHeader("Download?") + .setWidth("6rem"); + SearchFilterField searchFilterField = new SearchFilterField(); + @Getter + MediaLofiForm form; + MediaLofiService service; + + public MediaLofiView(MediaLofiService service) { + this.service = service; + addClassName("medialofi-view"); + setSizeFull(); + configureGrid(); + configureForm(); + + add(getToolbar(), getContent()); + updateList(); + } + + private void configureGrid() { + grid.addClassName("mediavideo-grid"); + grid.setSizeFull(); + grid.getColumns().forEach(col -> col.setAutoWidth(true)); + idColumn.setVisible(false); + grid.asSingleSelect().addValueChangeListener(event -> editMediaLofi(event.getValue())); + } + + private void configureForm() { + form = new MediaLofiForm(); + form.setWidth("75em"); + form.setVisible(false); + form.addSaveListener(this::saveMediaVideo); + form.addDeleteListener(this::deleteMediaLofi); + form.addCloseListener(e -> closeEditor()); + } + + private void saveMediaVideo(MediaLofiForm.SaveEvent event) { + service.saveMediaLofi(event.getMediaLofi()); + updateList(); + closeEditor(); + } + + private void deleteMediaLofi(MediaLofiForm.DeleteEvent event) { + service.deleteMediaLofi(event.getMediaLofi()); + 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() { + searchFilterField.addFilter("Überprüfung"); + searchFilterField.addFilter("Download"); + searchFilterField.addValueChangeListener(e -> updateList()); + + Button addMediaLofiButton = new Button("Add MediaLofi"); + addMediaLofiButton.addClickListener(click -> addMediaLofi()); + + Button menuButton = new Button("Show/Hide Columns"); + menuButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY); + ColumnToggleContextMenu columnToggleContextMenu = new ColumnToggleContextMenu<>(menuButton); + columnToggleContextMenu.addColumnToggleItem(idColumn); + columnToggleContextMenu.addColumnToggleItem(urlColumn); + columnToggleContextMenu.addColumnToggleItem(titleColumn); + columnToggleContextMenu.addColumnToggleItem(fileNameColumn); + columnToggleContextMenu.addColumnToggleItem(reviewColumn); + columnToggleContextMenu.addColumnToggleItem(shouldDownloadColumn); + HorizontalLayout toolbar = new HorizontalLayout(searchFilterField, addMediaLofiButton, menuButton); + toolbar.addClassName("toolbar"); + return toolbar; + } + + public void editMediaLofi(MediaLofi mediaLofi) { + if (mediaLofi == null) { + closeEditor(); + } else { + form.setMediaLofi(mediaLofi); + form.setVisible(true); + addClassName("editing"); + } + } + + private void closeEditor() { + form.setMediaLofi(null); + form.setVisible(false); + removeClassName("editing"); + } + + private void addMediaLofi() { + grid.asSingleSelect().clear(); + editMediaLofi(new MediaLofi()); + } + + public void updateList() { + grid.setItems(service.findAllMediaLofis(searchFilterField.getValue())); + } +}